Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Optimize a fast path for instanceof binary expressions #2506

Closed
wants to merge 6 commits into from

Conversation

trueadm
Copy link
Contributor

@trueadm trueadm commented Aug 29, 2018

Release notes: none

Looking through our internal bundles and there are frequent cases where instanceof is used where the left-hand side is a primitive and the right-hand side is an abstract value. In the case where the left-hand side is a primitive and the right-hand side is a simple object, the instanceof binary expression should always return false.

@trueadm trueadm requested a review from calebmer August 29, 2018 23:03
@sebmarkbage
Copy link
Contributor

So this is approaching a problem space we don't really talk about much but in ES2015 this assumption is actually not true:

let Stringish = {[Symbol.hasInstance](v) { return typeof v === 'string'; }};
'foo' instanceof Stringish; // true

@trueadm
Copy link
Contributor Author

trueadm commented Aug 29, 2018

@sebmarkbage This sucks a bit.

function fn(x) {
  var y = undefined;
  if (y instanceof x) {
    return y.somethingThatDoesNotExist;
  }
}

Prepack will spit out this:

  var _2 = function (x) {
    var _$0 = void 0 instanceof x;

    var _4 = (__constructor.prototype = _$3, new __constructor());

    $$0.value = "null or undefined", _$5(_4, "message", $$0);
    $$0.value = "TypeError: null or undefined\n    at fn (/Users/ADM/Projects/prepack/fb-www/input.js:6:12)", _$5(_4, "stack", $$0);
    if (_$0) throw _4;else {}
    return void 0;
  };

Which is somewhat problematic in the cases of code bloat. Interestingly, Closure Compiler also optimizes the same thing above away even though it supports ES2015.

@NTillmann
Copy link
Contributor

Ignoring the correctness issue (if we make unsound assumptions or optimizations, we need to document that somewhere!), how does this change relate to the simplifier? Shouldn't this be a simplification rule?

@trueadm
Copy link
Contributor Author

trueadm commented Aug 29, 2018

@NTillmann Nope, as this creates a temporal with createOperationDescriptor("BINARY_EXPRESSION", { binaryOperator: op })

@sebmarkbage
Copy link
Contributor

At the very least we can do this optimization for simple objects as the right hand.

For objects that are not topVal as the right hand, we should just continue executing since it'll resolve the real value.

If it's not an object on the right hand we can continue too since that'll throw.

If it's a topVal object on the right hand, we can issue a recoverable error. Since we can assume continue under the assumption it's false.

@trueadm
Copy link
Contributor Author

trueadm commented Aug 29, 2018

@sebmarkbage I've refined the code to add the simple check.

Copy link
Contributor

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

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

Let's document this somewhere.

if (
op === "instanceof" &&
lval instanceof PrimitiveValue &&
(rval instanceof ObjectValue || rval instanceof AbstractObjectValue) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

The rval instanceof ObjectValue is unnecessary since one of them needs to be an abstract value.

You could expand the lval to include abstract values with Value.isTypeCompatibleWith(lval.getType(), PrimitiveValue). That way you handle the abstract primitive case too.

https://github.com/facebook/prepack/blob/master/src/methods/to.js#L682

Copy link
Contributor

@hermanventer hermanventer left a comment

Choose a reason for hiding this comment

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

Seems safe to me.

Copy link

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

trueadm is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

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

Successfully merging this pull request may close these issues.

5 participants