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

Refactor and test custom marshalling logic #15113

Merged
merged 5 commits into from May 17, 2017

Conversation

pcardune
Copy link
Contributor

Here's what happened in the following commits:

  • Renamed PatchedInterpreter to CustomMarshalingInterpreter to better reflect that it's really an extension of the interpreter with customizations (which happen to include patches/overrides of the interpreter)
  • Converted monkey patches of get/set/hasProperty in JSInterpreter into regular method overrides in CustomMarhsalingInterpreter
  • Moved custom marshaling configuration and related logic into a new CustomMarshaler class
  • Added unit tests for:
    • the custom marshaling bit of codegen.marshalNativeToInterpreter
    • how properties are set on objects with different custom marshaling configurations
    • how objects actually get marshaled

There are probably still some more tests to write, but I think this represents a good checkpoint that is worth merging into staging.

@@ -300,8 +239,12 @@ export function marshalNativeToInterpreter(interpreter, nativeVar, nativeParentO
if (typeof maxDepth === "undefined") {
maxDepth = Infinity; // default to infinite levels of depth
}
if (shouldCustomMarshalObject(nativeVar, nativeParentObj)) {
return createCustomMarshalObject(interpreter, nativeVar, nativeParentObj);
// TODO (pcardune): remove circular dependency
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO: Address now, or in a near-future PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

This TODO is still around after your last batch of changes. I assume you'd like to address it in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes :)

@@ -0,0 +1,69 @@

module.exports = class CustomMarshaler {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not export default here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good question, will add a comment explaining. tl;dr: to make it work natively with node.js

nativeParentObj[marshalObj.requiredMethod] !== undefined) {
return marshalObj.methodOpts || {};
} else {
return {};
Copy link
Contributor

Choose a reason for hiding this comment

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

Having just extracted this helper, it'd be nice for it to have 100% test coverage.

isPrimitive: false,
isCustomMarshal: true,
type: typeof nativeObj,
parent: nativeParentObj, // TODO (cpirich): replace with interpreter object?
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this TODO worth keeping around anymore? (@cpirich?)

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 think this might break in strange ways with the latest interpreter version, which gets rid of the parent property entirely in favor of something a bit more complicated for which nativeParentObj probably doesn't make any sense.


constructor(code, customMarshaler, opt_initFunc) {
super(code);
if (!customMarshaler) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe better? if (!(customMarshaler instanceof CustomMarshaler))

constructor(code, customMarshaler, opt_initFunc) {
super(code);
if (!customMarshaler) {
throw new Error("You must provide a CustomMarshaler to CustomMarshalingInterpreter");
Copy link
Contributor

Choose a reason for hiding this comment

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

It'd be nice to cover this error case.

// this work from the parent class's constructor because it needs
// customMarshaler to have been set before-hand. Unfortunately, javascript
// does not allow us to set customMarshaler until after the parent constructor
// has finished.
Copy link
Contributor

Choose a reason for hiding this comment

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

This TODO does not actually describe an action item. It should maybe be updated to describe a desired outcome (refactor constructors to do less work, deduplicate code with parent constructor by extracting a helper, etc).

obj === this.globalScope &&
!!(nativeParent = this.customMarshaler.globalProperties[name]) &&
!this.shouldBlockCustomMarshalling_(name, obj, nativeParent)) {
customMarshalValue = nativeParent[name];
Copy link
Contributor

Choose a reason for hiding this comment

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

😆 I love that this one case of this method is not covered, especially because I can't really tell what this case is. Something about getting a custom-marshaled property from the interpreter's global scope?

if (!hasProperty &&
obj === this.globalScope &&
!!(nativeParent = this.customMarshaler.globalProperties[name]) &&
!this.shouldBlockCustomMarshalling_(name, obj, nativeParent)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This complex conditional gets reused in getProperty, hasProperty and setProperty. Let's extract a helper.

}
return;
}
scope = scope.parentScope;
Copy link
Contributor

Choose a reason for hiding this comment

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

It really seems like we should have a test over this method walking up the scope chain.

set customMarshalObjectList(customMarshalObjectList) {
// TODO: perform validation on this
this._customMarshalObjectList = customMarshalObjectList;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can these go away yet? They appear to get/set a property that's not used at all within the class, meaning anything calling them is probably broken anyway.

Copy link
Contributor

@islemaster islemaster left a comment

Choose a reason for hiding this comment

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

Hooray for better naming and new test coverage! 🎉 I've made some comments on cleanup and tightening up tests, but I see that you'd like to use this as a checkpoint. My only real concern before merge is whether the vestigial _customMarshalObjectList property in the interpreter is going to cause problems.

@pcardune
Copy link
Contributor Author

I think this got better since the last time you looked at this. PTAL?

* 4. If the property being looked up has been explicitly blocked from custom
* marshalling, then don't return the native parent.
*/
!this.shouldBlockCustomMarshalling_(name, obj, nativeParent)
Copy link
Contributor

Choose a reason for hiding this comment

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

🤣 Great commenting here. Scary to even have a conditional this complicated and subtle, but great explanation.

} else {
const nativeParent = this.getNativeParent_(obj, name);
if (nativeParent) {
customMarshalValue = nativeParent[name];
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not seeing coverage for these new files from the browser extension, not sure why. Anyway, it looks like this line still isn't covered. Have trouble tracking down this case?

Copy link
Contributor

@islemaster islemaster left a comment

Choose a reason for hiding this comment

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

This looks great!

@pcardune pcardune merged commit ff413c4 into staging May 17, 2017
@pcardune pcardune deleted the pcardune-custom-marshaling-interpreter branch May 17, 2017 20:47
@islemaster
Copy link
Contributor

@pcardune This change shipped yesterday. We got a Zendesk ticket this morning saying that the edges group (somehow added to the global scope by the createEdgeSprites() function in gamelab) has stopped working, and we repro. My suspicion is that it has something to do with this change. @caleybrock and I are investigating. Any idea what might have happened?

@islemaster
Copy link
Contributor

Looks like edges and some others are listed as "custom marshal global properties" for GameLabP5, even though some of them don't exist at initialization time.

}
} else {
if (this.getNativeParent_()) {
return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like getNativeParent should be called with parameters, like this: this.getNativeParent_(obj, name)
@islemaster is working on a fix

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants