Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do a better job preventing serialization of unnecessary objects in closure serialization #1543

Merged
merged 12 commits into from
Jun 20, 2018

Conversation

CyrusNajmabadi
Copy link
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Jun 19, 2018

I recommend reviewing https://github.com/pulumi/pulumi/pull/1543/files?w=1 as this will make the indentation changes tolerable.

I also recommend looking at parseFunction.ts first, then createClosure.ts.

// logInfo = logInfo || func.name === "addHandler";
if (logInfo) {
console.log("Serializing " + func.name);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is effectively debug-only code. I end up adding this in a bunch, and i wouldn't mind just leaving this in. Note that it will not do anything by default since logInfo=undefined. But this allows for a pretty easy way to opt into turning on printf-style logging when helpful.
We could also consider some sort of logging library, but i didn't want to go through the pain of doing that.

serialize: (o: any) => boolean): FunctionInfo {
serialize: (o: any) => boolean, logInfo?: boolean): FunctionInfo {

// logInfo = logInfo || func.name === "addHandler";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful for being able to turn on logging when serializing just a specific function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably leave this out or formalize it as part of the debug logging for this package.

// if this property was invoked or not.
for (const existingProp of combined) {
if (existingProp.name === current.name) {
existingProp.invoked = existingProp.invoked || current.invoked;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i removed this merging logic as it was too complex now that we have 'chains' of property-accesses recorded. Instead, now we just record all the chains, and we consider all of them (including dupes) when doing closure serialization. so if we have "a.b.c()" and "a.b.c" and "a.b.c", we'll record three chains of 'b.c' (with the first saying it is invoked). Then, in closure serialization, when we're emitting the value for 'c', we'll see if any of the accesses were 'invoked'.

Copy link
Member

@lukehoban lukehoban left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple notes. Generally this looks good - though there's a lot of subtle interactions here that are a little hard to track through. Glad to see the new tests!

Possibly @swgillespie can take a look as well.

serialize: (o: any) => boolean): FunctionInfo {
serialize: (o: any) => boolean, logInfo?: boolean): FunctionInfo {

// logInfo = logInfo || func.name === "addHandler";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably leave this out or formalize it as part of the debug logging for this package.

// validate our invariants.
for (const chain of localCapturedPropertyChains) {
if (chain.infos.length === 0) {
throw new Error("We should never have gotten an empty chain.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: "Expected a non-empty chain." or similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@swgillespie swgillespie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read through all of this but I need a little more time to fully digest it. I'll finish off the review first thing tomorrow.

context: Context,
serialize: (o: any) => boolean): Entry {
serialize: (o: any) => boolean,
logInfo: boolean | undefined): Entry {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logInfo?: boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like forcing people to have to remember to call it. logInfo?:boolean means it is optional, which means you might forget it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does typescript not consider T | undefined arguments to be optional?

Copy link
Contributor

@swgillespie swgillespie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I got it now - LGTM! The comments were very helpful 😄

Cyrus Najmabadi added 2 commits June 20, 2018 12:34
Serialize doNotCapture functions with a throwing function on the runtime side.
This will help people get a reasonable error at runtime instead of just a null-ref.
@CyrusNajmabadi
Copy link
Contributor Author

I took a 3-pronged attack here:

  1. improved tree-shaking so that we don't try to serialize out things we don't need. This is super useful since we commonly have new module1.module2.Type, and we really do not want to capture/serialize module1 or module2. (Previously the optimization only did this for the first name in the dotted chain.)
  2. I marked certain functions that cannot be serialized as doNotCapture. This way if something does end up pulling these in unintentionally, they do not break closure serialization.
  3. I improved how doNotCapture functions are serialized. Previously, we just ignored them entirely. Now we serialize them over as a function that throws, telling you what the problem is more clearly.

@CyrusNajmabadi CyrusNajmabadi merged commit 4cebc38 into master Jun 20, 2018
@CyrusNajmabadi CyrusNajmabadi deleted the justThrow branch June 20, 2018 19:58
@ellismg ellismg added the kind/bug Some behavior is incorrect or out of spec label Jun 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Some behavior is incorrect or out of spec
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants