Commit Graph

1558 Commits

Author SHA1 Message Date
Dominic Gannaway
119007f6fd Move _callOfFunction out and make general purpose (#2341)
Summary:
Release notes: none

I plan on re-using the same logic in `_callOfFunction` for nested optimized array method functions + some React stuff, so this PR makes the logic re-usable.
Pull Request resolved: https://github.com/facebook/prepack/pull/2341

Differential Revision: D9061066

Pulled By: trueadm

fbshipit-source-id: cc5c3efd579f4039eb45b14558ca434775a724d4
2018-07-30 11:08:42 -07:00
Dominic Gannaway
bd3c953189 Accounts for P in AbstractObjectValue being a SymbolValue (#2333)
Summary:
Release notes: none

Changes invariant to accept SymbolValue as well as strings. I found to be hitting this invariant in the internal bundle. I'll put up a regression test next week as this is a tricky one to repro it seems.
Pull Request resolved: https://github.com/facebook/prepack/pull/2333

Differential Revision: D9046722

Pulled By: trueadm

fbshipit-source-id: 1fc43f17c8e4e637aeaa3156319b6ae4995c7dad
2018-07-30 05:23:44 -07:00
Roman Khotsyn
71e955c979 Fixes & Improvements for ShapeInformation types (#2334)
Summary:
This PR consists of several changes to fix and improve shape modeling:
- Made all types exported to be use them in type-checking for InstantRender model generation
- Removed type information from link node (fix)
- Changed type name from `ShapeDescriptorOfPrimitive` to `ShapeDescriptorOfScalar` to make it clear
- Refined semantics of universe by allowing unknown (`undefined`) shapes in universe.
Pull Request resolved: https://github.com/facebook/prepack/pull/2334

Differential Revision: D9046663

Pulled By: hotsnr

fbshipit-source-id: fa197994bba3ccf7eb6b5d39b24861831c25089c
2018-07-30 04:54:20 -07:00
Sapan Bhatia
2b8ae77a33 Make it possible to materialize without havocing (#2325)
Summary:
This is Part 2/4 of #2185, which is being broken out into focused pieces. This piece separates materialization from the leaking and havocing logic, deals with flow issues and avoids increasing the maximum flow cycle size by adding the materialization routine to singletons.js.

But it does not introduce the possibly problematic pairing of ObjectValues with the generators in which they were leaked. Objects are materialized in the current generator, just as before.

trueadm needs this for #2321. I suggest he review it before NTillmann and hermanventer to check that it meets his needs.
Pull Request resolved: https://github.com/facebook/prepack/pull/2325

Differential Revision: D9039487

Pulled By: sb98052

fbshipit-source-id: d3c83aa6d8ef0939e8157ca19bd31265dd05e9c4
2018-07-27 18:38:53 -07:00
Caleb Meredith
c751410514 Keep key on inlined component (#2324)
Summary:
Fixes #2322. The React Compiler was discarding keys for components that were inlined.

Consider:

```jsx
const React = require("react");

__evaluatePureFunction(() => {
  function Lambda(props) {
    return [<div key="0" />, <Omega key="1" />, <Omega key="2" />];
  }

  function Omega(props) {
    return <div />;
  }

  __optimizeReactComponentTree(Lambda);

  module.exports = Lambda;
});
```

```jsx
(function() {
  var _2 = function(props, context) {
    _4 === void 0 && $f_0();
    return [_4, _7, _7]; // <------------------------- `_7` has no `key`!
  };

  var $f_0 = function() {
    _4 = <div key="0" />;
    _7 = <div />;
  };

  var _4;

  var _7;

  module.exports = _2;
})();
```

 ---

Cases where this can be an issue:

- First render mode and we need to hydrate instances based on keys (will this be a thing?)
- Compiled component is re-rendered and a list has a different order. List item components are expensive.
- We inline components with state.

Of course, it’s possible this may be a non-issue.

My solution for this was to wrap the inlined React Element in a `<React.Fragment>` with a key. So my input above becomes:

```jsx
[
  <div key="0" />,
  <React.Fragment key="1"><div /></React.Fragment>,
  <React.Fragment key="2"><div /></React.Fragment>,
]
```

Cloning inlined elements and adding keys is a fragile operation since the element may already have a key or the element may be some other React node like a portal. I opted for wrapping in `<React.Fragment>`. Note that this also means we can hoist `<div />`  in the example above.

Thoughts on adding an optimization which clones the element and adds `key={x}` in cases where this is possible? `<React.Fragment>` seems correct in all cases, but cloning with `key={x}` when possible seems like it might be faster.

Also happy to hear other suggestions for maintaining the key on inlined elements.

This has no impact on our internal bundle.
Pull Request resolved: https://github.com/facebook/prepack/pull/2324

Differential Revision: D9034942

Pulled By: calebmer

fbshipit-source-id: 2cdeec611a2c1f67059fa093684b5b51e0bbe809
2018-07-27 15:23:43 -07:00
Sebastian Markbage
d16750f5c8 Set on havoced object should target the receiver, not the own object (#2338)
Summary:
In case the havoced object has a setter on it.

This is fixes this particular bug but it's also useful to be able to pass the correct receiver to properties.js rather than unwrapping it like we do now. This will be evident in a follow up PR.

This also lets emitPropertyAssignment deal with abstract values which is a common pattern and will become more common with widened objects.
Pull Request resolved: https://github.com/facebook/prepack/pull/2338

Differential Revision: D9035746

Pulled By: sebmarkbage

fbshipit-source-id: 2abb1a3eb047de1739dec94259a803c4c45e416d
2018-07-27 15:09:10 -07:00
Sebastian Markbage
b03e018910 Symbol Coercion Should Always Throw (#2300)
Summary:
Coercing a symbol to a number or primitive implicitly always throws an error. We generated fatal compiler errors. Instead, this should just be treated as a throw.

This is neat because that means other serializers doesn't have to deal with the expressions involving symbols.
Pull Request resolved: https://github.com/facebook/prepack/pull/2300

Differential Revision: D9035734

Pulled By: sebmarkbage

fbshipit-source-id: 60c0d089e63b7b36b6757dab0c9928c19514c5d6
2018-07-27 15:09:10 -07:00
Sebastian Markbage
2334d329d1 Special case conditionals for $DefineOwnProperty, $SetPartial, $GetPartial and $Delete (#2329)
Summary:
Follow up to #2219

This also includes two relevant bonus fixes: 2aa1ad35a4 and e96ac8c0c1
Pull Request resolved: https://github.com/facebook/prepack/pull/2329

Differential Revision: D9033950

Pulled By: sebmarkbage

fbshipit-source-id: b28b608b7449f0bbd7781c7d6aab5717d638d904
2018-07-27 13:56:01 -07:00
Sebastian Markbage
042a71259a Try conditional branches separately if binary expression fails (#2332)
Summary:
This solves the remaining issues in #2300.

If a binary expression can't be resolved as pure (such as when applied to values of two different types), we can instead try conditions separately and join the effects.

If the left value is conditional, we try each possible value separately until we hit a non-conditional value. Then we try any conditional right values.
Pull Request resolved: https://github.com/facebook/prepack/pull/2332

Differential Revision: D9032487

Pulled By: sebmarkbage

fbshipit-source-id: 0f89a680ad0b710e90c1dd7a4c127c2247527c99
2018-07-27 13:18:09 -07:00
David Cai
bfe8cd26af Repro option creates package with sourcefiles, debugger startup script (#2289)
Summary:
Release Notes:

- Created DebugReproManager to capture all sourcefiles touched by Prepack (to include minimal subset of useful sourcefiles in the debug package)
- `--repro` splits into two: `--reproUnconditionally` which will create a debug package _regardless_ of Prepack's success/failure, and `--reproOnFatal`, which will _only_ create a debug package if Prepack outputs a `FatalError`.
- The debug package now includes all relevant sourcefiles (except for node modules), a copy of the version of Prepack (lib) that was used when the package was created, and a script to `yarn install` the relevant modules for the included version of Prepack, then start the Nuclide Prepack debugger with the proper parameters for files in the debug package (including original prepack arguments). This is in addition to the original input files.
- The impact of having the `DebugReproManager` on in `--reproOnFatal` mode all the time is negligible, as show in the table below. This flag will be always be enabled on Sandcastle builds so that failures are more easily debugged.
    - The time difference between no repro flag and `--reproOnFatal` seems like it can be written off as simple variance between runs. The larger increase when actually creating the zip comes from reading and zipping the files, which takes time proportional how many files are touched.
- SourceMapManager was refactored to not use `Invariant` or `SourceFile`s. This is so that `DebugReproManager` import it without increasing the flow cycle, and allows the `DebugReproManager` to be passed from Prepack to the CLI to create the repro package.
- The repro option introduces a potential for a subtle race condition that is addressed as follows:
    - The last `if (!success && reproMode === "none") process.exit(1);` must check reproMode because `generateDebugRepro` involves an async process (directory zipping). If there is an ongoing repro and the process exits, the repro may terminate prematurely, causing no repro to be generated. Instead, this only triggers if there is no repro -- if there is, the `generateDebugRepro` function will handle process exiting if it needs to.

Usage:
```node [prepack] [files to prepack] --reproOnFatal /Absolute/path/to/repro/bundle.zip --debugBuckRoot /buck/root```
or
```node [prepack] [files to prepack] --reproUnconditionally /Absolute/path/to/repro/bundle.zip --debugBuckRoot /buck/root```

Demo: https://www.dropbox.com/s/p62ves2p55fyyl7/--repro%20annotated%20demo.mp4?dl=0
Pull Request resolved: https://github.com/facebook/prepack/pull/2289

Differential Revision: D9002841

Pulled By: caiismyname

fbshipit-source-id: 623b362f963095f1cd8163684fd6e76596e7c4fc
2018-07-26 14:55:16 -07:00
Ziqi Chen
79bce3f1d7 accessibilityTraits + accessibilityComponentType >> accessibilityRole + accessibilityStates 2/3
Summary:
Previously, I created two props, `accessibilityRole` and `accessibilityStates` for view. These props were intended to be a cross-platform solution to replace  `accessibilityComponentType` on Android and `accessibilityTraits` on iOS.

In this stack, I ran a code mod to replace instances of the two old properties used in our codebase with the new ones.
For this diff, I did a search for all the remnant uses of `accessibilityComponentType` that was not caught by my script, and I manually changed them to `accessibilityRole` and `accessibilityStates`. If the same prop also set `accessibilityTraits` I also removed that here because the two new props works on both platforms.

It was difficult to write a script for this, because most of them were contextual changes.
Out of the contextual changes, most of them followed one of these two patterns:

Before:

```
const accessibilityComponentType = 'button';
const accessibilityTraits = ['button'];

if (this.props.checked) {
  accessibilityTraits.push('selected');
}
if (this.props.disabled) {
 accessibilityTraits.push('disabled');
}

      contentView = (
        <AdsManagerTouchableHighlight
          accessibilityComponentType={accessibilityComponentType}
          accessibilityTraits={accessibilityTraits}
```

After:
      const accessibilityRole = 'button';
      const accessibilityStates = [];

        if (this.props.checked) {
          accessibilityStates.push('selected');
        }
        if (this.props.disabled) {
           accessibilityStates.push('disabled');
        }

      contentView = (
        <AdsManagerTouchableHighlight
          accessibilityRole={accessibilityRole}
          accessibilityStates={accessibilityStates}

Before:

```
  <PressableBackground
          accessible={this.props.accessible}
          accessibilityLabel={this.props.accessibilityLabel}
          accessibilityTraits={this.props.accessibilityTraits}
```

After:

```
  <PressableBackground
          accessible={this.props.accessible}
          accessibilityLabel={this.props.accessibilityLabel}
          accessibilityRole={this.props.accessibilityRole}
          accessibilityRole={this.props.accessibilityStates}
```

In addition to changing the props on the components,
Another fix I had to do was to add props  accessibilityRole and accessibilityStates to components that don't directly inherit properties from view including text input and touchables.

Reviewed By: PeteTheHeat

Differential Revision: D8943499

fbshipit-source-id: fbb40a5e5f5d630b0fe56a009ff24635d4c8cc93
2018-07-25 23:40:48 -07:00
Jeffrey Tan
9dd47ce775 New alpha release
Summary: New alpha release

Reviewed By: simonhj

Differential Revision: D9004307

fbshipit-source-id: c17fd72c847cffd088835a22d95c1103ee0d7165
2018-07-25 16:25:55 -07:00
Jeffrey Tan
6bf4c49713 Weekly release v0.2.45
Summary:
* Enhanced dead code elimination for optimized functions
* Much of the buildNode and inline Babel logic has been moved to a dedicated ResidualOperationSerializer class
* Provide a way to temporarily disable effects tracking
* Simplified forked completion constructors
* React components can have their props modelled via `__optimizeReactComponentTree`

Reviewed By: simonhj

Differential Revision: D9004189

fbshipit-source-id: 80936cbc66ad9dc350bbf0dcf0f1ff228a147f43
2018-07-25 16:25:55 -07:00
Caleb Meredith
93b8e764b5 Add message to error stack (#2311)
Summary:
Because of an ordering bug messages weren’t being added to the error stacks thrown by Prepack. This made debugging runtime issues in generated Prepack code difficult.

```js
throw new Error("foo");
```

```js
(function() {
  var $$0 = {
    enumerable: false,
    configurable: true,
    writable: true,
  };

  var _$0 = this;

  var _$1 = _$0.Error;
  var _$2 = _$1.prototype;
  var _$3 = _$0.Object;
  var _$4 = _$3.defineProperty;

  var __constructor = function() {};

  var _0 = ((__constructor.prototype = _$2), new __constructor());

  ($$0.value = "Error\n    at /Users/calebmer/prepack/fb-www/input.js:4:7"), _$4(_0, "stack", $$0);
  ($$0.value = "foo"), _$4(_0, "message", $$0);
  throw _0;
}.call(this));
```

```js
(function() {
  var $$0 = {
    enumerable: false,
    configurable: true,
    writable: true,
  };

  var _$0 = this;

  var _$1 = _$0.Error;
  var _$2 = _$1.prototype;
  var _$3 = _$0.Object;
  var _$4 = _$3.defineProperty;

  var __constructor = function() {};

  var _0 = ((__constructor.prototype = _$2), new __constructor());

  ($$0.value = "foo"), _$4(_0, "message", $$0);
  ($$0.value = "Error: foo\n    at /Users/calebmer/prepack/fb-www/input.js:4:7"), _$4(_0, "stack", $$0);
  throw _0;
}.call(this));
```
Pull Request resolved: https://github.com/facebook/prepack/pull/2311

Differential Revision: D9005517

Pulled By: calebmer

fbshipit-source-id: 8567624a67a78085d4072560541785e412cd6645
2018-07-25 16:25:55 -07:00
Sapan Bhatia
3bcd3fb4ee Deduplicate object creation code in a non-branched setting (#2302)
Summary:
This pull request breaks out the first part of #2185, which deduplicates object creation when leaking is unconditional to begin with. No changes are made to how leaking works. Only to how leak information is used by the serializer and visitor.
Pull Request resolved: https://github.com/facebook/prepack/pull/2302

Differential Revision: D8997109

Pulled By: sb98052

fbshipit-source-id: e0ea2e7ea574a9f2be71b70be289831c7f797d9d
2018-07-25 11:40:50 -07:00
Dominic Gannaway
b0fe89e863 Add shape model functionality to React components (#2320)
Summary:
Release notes: React components can have their props modelled via `__optimizeReactComponentTree`

This extends the current shape modelling functionality added for InstantRender (great work by hotsnr ) and provides the same powers to the React compiler. An example of usage:

```js
var React = require("React");

function App(props) {
  return <div>
    <h1>{props.header.toString()}</h1>
    <ul>
      {
        props.items && props.items.map(item =>
          <li key={item.id}>{item.title.toString()}</li>
        )
      }
    </ul>
  </div>;
}

App.getTrials = function(renderer, Root) {
  var items = [{ id: 0, title: "Item 1" }, { id: 1, title: "Item 2" }, { id: 2, title: "Item 3" }];
  renderer.update(<Root items={items} header={"Hello world!"} />);
  return [["render simple with props model", renderer.toJSON()]];
};

if (this.__optimizeReactComponentTree) {

  let universe = {
    Item: {
      kind: "object",
      jsType: "object",
      properties: {
        id: {
          shape: {
            kind: "scalar",
            jsType: "integral",
          },
          optional: false,
        },
        title: {
          shape: {
            kind: "scalar",
            jsType: "string",
          },
          optional: false,
        },
      },
    },
    Props: {
      kind: "object",
      jsType: "object",
      properties: {
        header: {
          shape: {
            kind: "scalar",
            jsType: "string",
          },
          optional: false,
        },
        items: {
          shape: {
            kind: "array",
            jsType: "array",
            elementShape: {
              shape: {
                kind: "link",
                shapeName: "Item",
              },
              optional: false,
            },
          },
          optional: true,
        },
      },
    },
  };

  let appModel = {
    component: {
      props: "Props",
    },
    universe,
  };

  __optimizeReactComponentTree(App, {
    model: JSON.stringify(appModel),
  });
}

module.exports = App;
```
Pull Request resolved: https://github.com/facebook/prepack/pull/2320

Differential Revision: D8996429

Pulled By: trueadm

fbshipit-source-id: 31eb4b42fcfab4aec785492ed0baecfb5533a0e2
2018-07-25 11:27:47 -07:00
Caleb Meredith
2810294260 Fix React branch serialization issues (#2318)
Summary:
After adding classes to my fuzzer (which does not use first render mode) I found #2316. This PR fixes #2316 and a related issue I found while working on that. (Each fix is in a separate commit.) The second issue I found is scarier since the compiler passes but we get invalid output.

In the following example I observed an invariant violation:

```js
const React = require("react");

__evaluatePureFunction(() => {
  function Tau(props) {
    return React.createElement(
      "div",
      null,
      React.createElement(Epsilon, {
        a: props.z,
      }),
      React.createElement(Zeta, {
        p: props.h,
      })
    );
  }

  class Epsilon extends React.Component {
    constructor(props) {
      super(props);
      this.state = {};
    }

    render() {
      return React.createElement(Zeta, { p: this.props.a });
    }
  }

  function Zeta(props) {
    return props.p ? null : React.createElement("foobar", null);
  }

  __optimizeReactComponentTree(Tau);

  module.exports = Tau;
});
```

```
=== serialized but not visited values
=== visited but not serialized values
undefined, hash: 792057514635681
  referenced by 1 scopes
      =>_resolveAbstractConditionalValue alternate(#75)=>ReactAdditionalFunctionEffects(#80)

Invariant Violation: serialized 26 of 27
This is likely a bug in Prepack, not your code. Feel free to open an issue on GitHub.
    at invariant (/Users/calebmer/prepack/src/invariant.js:18:15)
    at ResidualHeapSerializer.serialize (/Users/calebmer/prepack/src/serializer/ResidualHeapSerializer.js:2465:17)
    at statistics.referenceCounts.measure (/Users/calebmer/prepack/src/serializer/serializer.js:259:15)
    at PerformanceTracker.measure (/Users/calebmer/prepack/src/statistics.js💯14)
    at ast (/Users/calebmer/prepack/src/serializer/serializer.js:238:38)
    at statistics.total.measure (/Users/calebmer/prepack/src/serializer/serializer.js:169:17)
    at PerformanceTracker.measure (/Users/calebmer/prepack/src/statistics.js💯14)
    at Serializer.init (/Users/calebmer/prepack/src/serializer/serializer.js:136:35)
    at prepackSources (/Users/calebmer/prepack/src/prepack-standalone.js:66:33)
    at compileSource (/Users/calebmer/prepack/scripts/debug-fb-www.js:92:18)
```

Somehow we were visiting `undefined`, but clearly we weren’t serializing it given the source code. Here’s what was happening:

- We’d visit an additional function calling `withCleanEquivalenceSet`.
- The additional function would enqueue an action which visited `<foobar>`.
- Later, we’d execute the `<foobar>` visiting action outside our `withCleanEquivalenceSet` with our default equivalence set.
- The same thing happens with our new root from our `Epsilon` class.
- Except now some effects have been applied that set the `type` for our `<foobar>` React element in our React equivalence set to `undefined`. Since when we created `<foobar>` we modified its `type` property. (Recorded in `modifiedProperties`.)
- Now our `<Zeta>` in `<Tau>` and our `<Zeta>` in `<Epsilon>` share the _exact same_ `<foobar>` thanks to our equivalence set.
- But `<foobar>` has a `type` of `undefined` thanks to the effects we applied. We should be creating a new `<foobar>` since we are in a new optimized function.
- ***Boom!*** We visit `undefined`, but don’t serialize it since the same effects aren’t applied when we serialize.

This test case caught an important flaw in our visiting logic, but it only manifested as an invariant under these very specific conditions. Which is a little scary. In a large example, like our internal bundle, we would of course serialize some `undefined` value but we would have still visited `undefined` instead of the proper type, _and_ we may consider two elements to be equivalent when we shouldn’t since their components may render independently. This issue (I presume) can also affect we bail out on since they create new trees inside the root tree.

While debugging and fixing this issue, I found another with incorrect/suboptimal output that passes Prepack and passes eslint. Given the following input:

```js
require("react");

__evaluatePureFunction(() => {
  const React = require("react");

  function Tau(props) {
    return React.createElement(
      "a",
      null,
      React.createElement("b", null),
      React.createElement(Epsilon, null),
      React.createElement("c", null)
    );
  }

  class Epsilon extends React.Component {
    constructor(props) {
      super(props);
      this.state = {};
    }

    render() {
      return React.createElement("d", null);
    }
  }

  __optimizeReactComponentTree(Tau);

  module.exports = { Tau, Epsilon };
});
```

We get this output:

```js
(function () {
  var _$0 = require("react").Component;

  var _3 = function (props, context) {
    _6 === void 0 && $f_0();
    _9 === void 0 && $f_1();

    var _8 = <_B />;

    var _4 = <a>{_6}{_8}{_9}</a>;

    return _4;
  };

  var _B = class extends _$0 {
    constructor(props) {
      super(props);
      this.state = {};
    }

    render() {
      return _E; // <--------------------------- Incorrect. `_B` may be rendered outside of `_3`.
    }
  };

  var $f_0 = function () {
    _6 = <b />;
    _E = <d />;
  };

  var _6;

  var _E;

  var $f_1 = function () {
    _9 = <c />;
  };

  var _9;

  var _0 = {
    Tau: _3,
    Epsilon: _B
  };
  module.exports = _0;
})();
```

This happened because the React serializer’s `_lazilyHoistedNodes` logic was implemented in a way that doesn’t play well with the interleaving almost random order of the serializer invocation of React lazily hoisted nodes logic.
Pull Request resolved: https://github.com/facebook/prepack/pull/2318

Differential Revision: D8992253

Pulled By: calebmer

fbshipit-source-id: 4a75e5768ffb7887c3a8afa2a0f3f59e7eac266d
2018-07-25 09:55:21 -07:00
Herman Venter
a1bd30ddb9 Simplify forked completion constructors (#2315)
Summary:
Release note: Simplified forked completion constructors

Since effects and completions are now 1-1, we can stop passing (completion, effects) argument pairs.
Pull Request resolved: https://github.com/facebook/prepack/pull/2315

Differential Revision: D8985970

Pulled By: hermanventer

fbshipit-source-id: 12ab3848951be4fee973bde71eaca135f56b1a3d
2018-07-24 18:25:34 -07:00
Chris Blappert
62b11dcb6e Improved reported error for PP1003 (#2305)
Summary:
Previous error message:
`Property access conflicts with write in optimized function (unknown function) at 11:11 to 11:16`

New error message:
`Write to property "func" on <unnamed object> at optimized function fn[20:13 22:22] conflicts with access in function nested[11:11]`

Output generated off "Failure 2" in issue #2252
Pull Request resolved: https://github.com/facebook/prepack/pull/2305

Differential Revision: D8979410

Pulled By: cblappert

fbshipit-source-id: a4f507d8a77a44a0f4ca7619faf3a9fe03dabf45
2018-07-24 14:27:28 -07:00
Herman Venter
66115d4377 Cleanup Effects cloning (#2314)
Summary:
Release note: none

This is just a small code cleanup. Instead of cloning an Effects instance by directly calling the constructor of the clone, now call a clone method and supply only the parameter we want to change.

This is a mechanical refactor.
Pull Request resolved: https://github.com/facebook/prepack/pull/2314

Differential Revision: D8975311

Pulled By: hermanventer

fbshipit-source-id: eb57048782527de0756095f9efaee43cbcc1578b
2018-07-24 11:10:28 -07:00
Dominic Gannaway
789c698358 Cleanup of generator.js and moves out Babel serialization logic (#2306)
Summary:
Release notes: none

This cleans up generator.js in the following ways:

- pulls our NameGenerator and PreludeGenerator into it's own module
- takes out the Babel coupling with Generator and puts the serialization logic into the serializer instead

This is of many PRs to help clean up the serialization/generator logic.
Pull Request resolved: https://github.com/facebook/prepack/pull/2306

Differential Revision: D8961658

Pulled By: trueadm

fbshipit-source-id: 4aa3f34c2df1b26eef5719d1a49a5985b9a0e7b3
2018-07-23 17:46:33 -07:00
Caleb Meredith
d3e62037aa Support string with abstract argument (#2310)
Summary:
I was seeing `throwIfNotConcrete` a lot for `String()` calls on abstract values in #2297. The fix is relatively quick.
Pull Request resolved: https://github.com/facebook/prepack/pull/2310

Differential Revision: D8960761

Pulled By: calebmer

fbshipit-source-id: f378092cb3e36ac4026404a9e185abe9a768f577
2018-07-23 17:25:02 -07:00
Dominic Gannaway
2735f970a9 Refine operationDescriptor "data" and remove some properties (#2308)
Summary:
Release notes: none

This refines the `operationDescriptor`  `data` object and removes a bunch of properties, but mainly `propName`. Instead we now feed this around in args, mainly as a `StringValue` removing a bunch of confusing existing logic.
Pull Request resolved: https://github.com/facebook/prepack/pull/2308

Differential Revision: D8959081

Pulled By: trueadm

fbshipit-source-id: 6bb1e95735dcbe3a88800b94af71d6681692f432
2018-07-23 15:54:15 -07:00
Simon Jensen
5f3d54775f Assume that Object.assign is present in the "mobile" environment.
Summary: Pull Request resolved: https://github.com/facebook/prepack/pull/2313

Differential Revision: D8958641

Pulled By: simonhj

fbshipit-source-id: d5ff109f5a68eda818770a56196091b162db8610
2018-07-23 15:25:51 -07:00
Herman Venter
7480608e79 Provide a way to temporarily disable effects tracking (#2304)
Summary:
Release note: Provide a way to temporarily disable effects tracking

The default error handler constructs a new Error object in order to reuse its logic for obtaining a call stack. Since the object is then thrown a away, it makes no sense to keep tracking its properties and doing so makes debugging more painful.
Pull Request resolved: https://github.com/facebook/prepack/pull/2304

Differential Revision: D8945121

Pulled By: hermanventer

fbshipit-source-id: 68fb8b84c2ef937fcdf3ecaccdff5041779424d5
2018-07-20 18:27:57 -07:00
Chris Blappert
f80b4af956 ForkedAbruptCompletion created with generator containing joinCondition (#2272)
Summary:
Release Notes: None

PR solved by hermanventer's recent PR, just adding another test case closer to the one in the issue.

Resolves #2248
Pull Request resolved: https://github.com/facebook/prepack/pull/2272

Differential Revision: D8938294

Pulled By: cblappert

fbshipit-source-id: 18e4fd3b8913400b98242eff06f011ed0140459b
2018-07-20 12:09:39 -07:00
Dominic Gannaway
19272b5725 Follow up renaming GeneratorEntry (#2298)
Summary:
Release notes: none

Follow up to https://github.com/facebook/prepack/pull/2296. Renames some classes and types to the new naming format, rather than BuildNode.
Pull Request resolved: https://github.com/facebook/prepack/pull/2298

Differential Revision: D8930893

Pulled By: trueadm

fbshipit-source-id: a3b86cfc6670da67222c960818e6c186af05d271
2018-07-20 01:24:33 -07:00
Dominic Gannaway
bf0ed59174 Re-design of the buildNode logic for AbstractValue and GeneratorEntries (#2296)
Summary:
Release notes: much of the buildNode and inline Babel logic has been moved to a dedicated ResidualOperationSerializer class

Note: this PR is like *phase 1* of many phases to move around buildNodes and their logic. Furthermore, the ideas is to move all Babel coupled logic to the serialization phase and away from the generator and other generic areas. I've explained this more in detail below.

This PR moves much of the Babel logic involving build nodes, that was coupled to inline closures through many parts of the Prepack code base, to its own class `ResidualOperationSerializer`. We now use a helper function `createOperationDescriptor` to create `OperationDescriptor`s. `OperationDescriptor`s exist on both `AbstractValue` and `GeneratorEntry`, and replaces the old `buildNode` and `_buildNode` properties and unifies all the logic. The `buildNode` field has now changed to `operationDescriptor` respectfully.

We now have a `OperationDescriptorType` enum that tells the class which build node to use, along with some associated data that is needed to construct the build node (previously this data was captured in the closure). The idea is to remove the data/kind arguments from `createOperationDescriptor` in follow up PRs, but that will take quite a bit more work, thus why this is a phase 1 PR.

In many cases, we now have no instances of importing Babel in many of the files and this unifies much (but not all) of the Babel node creation logic into a single file – allowing for easy extension in the future.

This PR took forever to do and is very large, so there might be bits that I missed, but hopefully I haven't. All tests etc pass locally.
Pull Request resolved: https://github.com/facebook/prepack/pull/2296

Differential Revision: D8923889

Pulled By: trueadm

fbshipit-source-id: aa3a27efd97f10cc941159735d598199deb24f52
2018-07-19 15:41:15 -07:00
Caleb Meredith
108dea533b Add serializer mode for test262 execution (#2290)
Summary:
I want to better understand the bugs we have in abstract evaluation. One of my ideas to do this is to replace all the literals in test262 tests with abstract values and see what our test coverage is. The first step to do this is to serialize the test262 sources after running them in Prepack and checking if their generated JavaScript runs correctly. This PR does that by adding a `--serializer` flag.

With my methodology, all test262 harnesses are serialized every time since they are in the global scope. This unfortunately seems to be unavoidable since doing otherwise would change program semantics and break the tests.

Running `time yarn test-test262` takes about 2 minutes and has a 98% pass rate. Running `time yarn test-test262 --serializer` takes about 5 minutes and has a 95% pass rate. [Here’s a diff of the two  results.](https://gist.github.com/calebmer/1c9fe396b63ba055458c599c2be18a58)

[Here is a selection of some of the bugs](https://gist.github.com/calebmer/1ac381096a4aa7be1fc7dc2163276ab4) in the serializer caught by running test262 with the Prepack serializer. I might open issues for these, but they can be a bit pedantic. I might fix some of them depending on how important they are to the React code we want to compile.

Notably we have:

- Lots of invariants being triggered. I particularly saw [a lot of this invariant](7d355ef4c5/src/serializer/ResidualHeapVisitor.js (L531)). (An example of this is [`07.md`](https://gist.github.com/calebmer/1ac381096a4aa7be1fc7dc2163276ab4#file-07-md).)
- Lots of values that are visited, but not serialized. (An example of this is [`01.md`](https://gist.github.com/calebmer/1ac381096a4aa7be1fc7dc2163276ab4#file-01-md).)
- Some incorrect outputs only caught by executing code. Could not be caught by static analysis. Particularly around class serialization.

I’ll build off this PR to get coverage for the abstract evaluator next, but I found these results interesting on their own so decided to do this in two PRs.
Pull Request resolved: https://github.com/facebook/prepack/pull/2290

Differential Revision: D8908479

Pulled By: calebmer

fbshipit-source-id: aa57d47611fbd92af33e4647fed7bf7990fb6de1
2018-07-18 18:54:51 -07:00
Nikolai Tillmann
eeb081a545 Don't emit modified bindings unless they are alive (#2287)
Summary:
Release notes: Enhanced dead code elimination for optimized functions

This resolves #2276: Visiting+emitting modified bindings now participates
in the fixed point computation, checking for whether the modified binding
is actually getting visited for other reasons.

The previously existing code that did CSE by mutating the generator entry
got removed, as it wouldn't work exactly like that anymore (can't return value
because actual analysis gets postponed for fixed point computation). Filed #2286
to keep track of that and more needed changes around CSE for values stored in locations
participating in effects tracking. --- Turns out that we didn't have a test anyway
that would detect whether binding CSE happens or not.

Added regression test.
Pull Request resolved: https://github.com/facebook/prepack/pull/2287

Differential Revision: D8902345

Pulled By: NTillmann

fbshipit-source-id: 6ba3fb9755312956829050f0278dee2d85d6a261
2018-07-18 14:40:05 -07:00
Nikolai Tillmann
7d355ef4c5 Ignore modifications of Prepack-specific intrinsic properties (#2269)
Summary:
Release notes: None

Fixes #2266 and adds regression test.
Fixes #2262 and adds regression test.
Pull Request resolved: https://github.com/facebook/prepack/pull/2269

Differential Revision: D8896059

Pulled By: NTillmann

fbshipit-source-id: aee93083c6da6d703d7788a6458e2506638debc3
2018-07-18 10:54:59 -07:00
Chris Blappert
237d825dae Pretty Print Generators and Effects (#2270)
Summary:
In an effort to make debugging easier, add utilities that help pretty-print generators and effects with all the important fields (and none of the internal ones that you see in a debugger). Depth of recursion of printer is customizable.

Currently doesn't pass Flow because I have a helper function that takes an Object and returns an Object. Not sure how to make that type more specific.

Open to feedback on ways to change the output to be more readable/useful.

Output looks like this for a simple set of effects:
```
{
  result: [ReturnCompletion value [Abstract 1735003607742184]],
  generator: {
    _entries: {
      index: 39,
      declared: [Abstract 1735003607742184],
      args: []
    },
    id: 35,
    _name: evaluateNodeForEffects,
    pathConditions: [[Abstract 1735003607742177], [Abstract 1735003607742183]]
  },
  canBeApplied: true,
  _id: 33
}
```
Pull Request resolved: https://github.com/facebook/prepack/pull/2270

Differential Revision: D8896030

Pulled By: cblappert

fbshipit-source-id: 4f15a047b43d41ccad53222850fad2bf3216b08c
2018-07-18 10:54:59 -07:00
baptistemanson
e05835ce69 Updated the code of conduct links. (#2284)
Summary:
Release notes: None

I updated the code of conduct links, the old ones were 404.
Please note ReactJS and Prepack have different CoC.
Pull Request resolved: https://github.com/facebook/prepack/pull/2284

Differential Revision: D8895997

Pulled By: sb98052

fbshipit-source-id: 90d84fa6215b9d1f1705de43511552e78be8dd00
2018-07-18 10:39:53 -07:00
Dan Abramov
3bf79dd693 Add a regression test for #2090 (#2281)
Summary:
https://github.com/facebook/prepack/issues/2090 doesn't reproduce for me anymore so I'm adding a test.
Pull Request resolved: https://github.com/facebook/prepack/pull/2281

Differential Revision: D8893196

Pulled By: gaearon

fbshipit-source-id: c0d9748e5898ab8cffa0c6c23dd5db9dadfbf6cc
2018-07-18 07:24:17 -07:00
Sapan Bhatia
6cdf42f9e1 Bump version for next alpha
Summary: Bump version of alpha version of Prepack.

Reviewed By: gaearon

Differential Revision: D8892572

fbshipit-source-id: 129a265b5c8a725d4a09daa6811c0cc1a7ba2249
2018-07-18 07:24:17 -07:00
Sapan Bhatia
1d51aed47b Weekly release v0.2.44: Function argument modeling
Summary:
- Support modeling of shapes of optimized function arguments
- fix test262 to fail CircleCI test if not enough tests pass
- upgrades Prepack to use Babel 7.0.0-beta.53
- Nuclide compatibility fix

Reviewed By: gaearon

Differential Revision: D8892151

fbshipit-source-id: 8f1397d25d26e822709ed53c8e6d37055fe396ec
2018-07-18 07:24:17 -07:00
Dan Abramov
bdbce00f53 Use Flow server for local development (#2245)
Summary:
`flow check` runs a complete Flow check.
This is good for CI, but it means every re-check after edit is as slow as first check.

I'm changing local `yarn flow` to use a Flow server instead. This should speed up rechecks locally somewhat. Still won't save us from large recheck cycles, but at least we're not starting from a clean slate every time.

I'm adding `yarn flow-ci` with old behavior for the CI.

Finally, I replaced `--merge-timeout` with an equivalent recently added config option. This ensures it's respected both for the server and for the full check. Without it, the server dies.
Pull Request resolved: https://github.com/facebook/prepack/pull/2245

Differential Revision: D8891234

Pulled By: gaearon

fbshipit-source-id: 2c309a1718ed00ca5839cb3a586386e3e779f029
2018-07-18 06:40:25 -07:00
Roman Khotsyn
6e60644e12 Function argument modeling (#2215)
Summary:
This change allows modeling of optimized function arguments in different way than regular environment modeling. The main point of this modeling is being able to determine what getter should be used at this particular GraphQL property access at compile time. To achieve this Prepack should know the shape of arguments used for optimizing functions and should be able to infer the shape of value when it is needed.
Shape information is attached to AbstractValue (to pass it around) and on every property access a new AbstractValue is returned with shape information specific to this property.
As an output of this process, every member access to modeled values is replaced by function call like prop_string(obj, "key").
Structure of the model can be found in `ShapeInformation.js`.
Pull Request resolved: https://github.com/facebook/prepack/pull/2215

Reviewed By: NTillmann

Differential Revision: D8874743

Pulled By: hotsnr

fbshipit-source-id: 9e1b2254ef54986229be7d1195c1586b95d9a4be
2018-07-18 04:09:03 -07:00
Nikolai Tillmann
7ec32cf3aa For abstract objects, look into values domain for IsCall (#2268)
Summary:
Release notes: None

IsCall used to fail on abstract values even if they have a set of values in their domain.
This in turn would make `typeof` not work on abstract values.
This makes it work, and adds a regression test.
Pull Request resolved: https://github.com/facebook/prepack/pull/2268

Differential Revision: D8878641

Pulled By: NTillmann

fbshipit-source-id: f6dda513786eaf3f02bd13d5c6474c2f20fb0082
2018-07-18 01:54:08 -07:00
Herman Venter
cd67ae0e8c Move generators from PNC normal branches to joined normal effects (#2274)
Summary:
Release note: none

Generators that end up in joined effects, need to disappear from the generators in the forked completions from which they have been extracted. Also, they need to be applied to the generator containing the timeline in which the fork occurs, before the fork occurs.

With some luck, this may fix a lot of issues.
Pull Request resolved: https://github.com/facebook/prepack/pull/2274

Differential Revision: D8878320

Pulled By: hermanventer

fbshipit-source-id: a6cd41bf9e82bc4e362bda32a1e32c5fc90298cf
2018-07-17 19:09:35 -07:00
Caleb Meredith
b0516b49ea Nested abstract conditional fixes (#2255)
Summary:
Fixes #1867 and fixes #2058 which are issues blocking the React compiler.

Test case from #1867 copied below:

```js
(function() {
  function fn(arg) {
    if (arg.foo) {
      if (arg.bar) {
        return 42;
      }
    }
  }

  if (global.__optimize) {
    __optimize(fn);
  }

  global.inspect = function() {
    return JSON.stringify([
        fn(null),
        fn({}),
        fn({foo: true}),
    ]);
  };
})();
```

The fix is almost entirely in `joinOrForkResults` where I remove the call to `updatePossiblyNormalCompletionWithConditionalSimpleNormalCompletion` and replace it with a new `PossiblyNormalCompletion`. Here’s my understanding of the issue and the previous strategy. Hopefully hermanventer can correct me if I’m wrong or explain why it needs to be the way it was.

In our program above when we are joining the `arg.foo` consequent with its alternate we have the following completion for the `arg.foo` consequent:

- `PossiblyNormalCompletion` (join condition is `arg.bar`)
  - `ReturnCompletion(42)` (value of `42`)
  - `SimpleNormalCompletion(empty)` (there is no `else`)

We are joining this with an alternate of `SimpleNormalCompletion` since there is no `else` on the `arg.foo` if-statement. Before we would try and “sneak into” the two branches and add an abstract conditional to the result. However, the implementation was limited so we we only updated `SimpleNormalCompletion`. This meant we ended up changing our above completion to:

- `PossiblyNormalCompletion` (join condition is `arg.bar`)
  - `ReturnCompletion(42)`
  - `SimpleNormalCompletion(arg.foo ? empty : empty)`

The only change being the second `SimpleNormalCompletion` is now `arg.foo ? empty : empty`. This would serialize into roughly:

```js
function f() {
  return _$1 ? 42 : undefined;
}
```

Where `_$1` is not declared! Since we were serializing a `PossiblyNormalCompletion` with a join condition of `arg.bar` and the `arg.foo ? empty : empty` abstract conditional simplified to just `empty` which means we never emit `arg.foo` so never emit `arg.bar` which has a dependency on `arg.foo`.

My strategy was to nest the `PossiblyNormalCompletion` in another `PossiblyNormalCompletion`. So the final completion for the code sample at the top of this PR is:

- `PossiblyNormalCompletion` (join condition is `arg.foo`)
  - consequent: `PossiblyNormalCompletion` (join condition is `arg.bar`)
    - consequent: `ReturnCompletion(42)`
    - alternate: `SimpleNormalCompletion(empty)`
  - alternate: `SimpleNormalCompletion(empty)`

I’m confused as to why this was not done in the first place and instead an update strategy was used.

I then wrote a test (now in `test/serializer/optimized-functions/NestedConditions.js`) to enumerate a bunch of nested conditional cases. The rest of the fixes come from there. Notably the refactor of `replacePossiblyNormalCompletionWithForkedAbruptCompletion` which did not produce the same output when `consequent` and `alternate` were flipped.
Pull Request resolved: https://github.com/facebook/prepack/pull/2255

Differential Revision: D8865130

Pulled By: calebmer

fbshipit-source-id: 4964acc11b6403b9956cf29decefb1971340516d
2018-07-17 15:18:34 -07:00
Dominic Gannaway
5f626ebdbf Ensure Object.assign merge optimization properly accounts for prefix args (#2261)
Summary:
Release notes: none

When we check to do the temporal `Object.assign` merge optimization, we should properly account for any "prefix" args that occur before "to" and the "suffix" args. Test case added that came up in testing.
Pull Request resolved: https://github.com/facebook/prepack/pull/2261

Differential Revision: D8874714

Pulled By: trueadm

fbshipit-source-id: 59869f556c1300424c5a1b59d3df1fdd139b41fe
2018-07-17 06:24:02 -07:00
Herman Venter
2dc8607269 Compose subsequent effects with currently applied affects (#2271)
Summary:
Release note: none

This fixes a problem reported as a comment in PR #2258.

The underlying problem was that Realm.evaluateForEffects would just append the normal effects, captured since the last non complete join, into the saved partially normal completion that becomes the result of the returned Effects. This is not quite right because applying the resulting Effects should restore the complete set of normal effects that were in force when evaluateForEffects completed.
Pull Request resolved: https://github.com/facebook/prepack/pull/2271

Differential Revision: D8870763

Pulled By: hermanventer

fbshipit-source-id: 027924f2b250174393c60d90c2feefdb5a55eec3
2018-07-16 18:25:24 -07:00
Herman Venter
bad3985f54 Clone completions when they complete composed effects (#2258)
Summary:
Release note: none

The main part of this PR is to ensure that completions and the effects they complete are always 1-1.

Along the way some bugs got fixed #2241.

This also includes a little side project: limiting the console spew when doing "yarn test-serializer --fast", which is something I do all the time and I'm much happier this way.
Pull Request resolved: https://github.com/facebook/prepack/pull/2258

Differential Revision: D8864448

Pulled By: hermanventer

fbshipit-source-id: a7f257a7e07211ecd6069b84330aa3305609c5d2
2018-07-16 13:56:10 -07:00
Sebastian Markbage
81bc21fad8 Delete node-cli Option and all the Node.js intrinsics (#2267)
Summary:
Since I'm adding a new experiment I figured I'd delete an equivalent sized one.

Last year I added an option that runs the Prepack program by invoking Node.js JS runtime which lets us prepack the whole module system and initialization. It's essentially a packager with perfect Node.js module resolution semantics. It did this by modeling Node's native environment as Prepack bindings.

This PR removes that whole option.

There's a few reasons why I don't think that worked out as a good idea.

- It's not solving a real need. It is hard to keep different module systems in tact. There is always something in the ecosystem that breaks down and using the canonical one solves that. However, in practice, if there is a need for bundling the ecosystem itself adapts to the toolchain. So it's not actually that hard to bundle up a CLI even with Webpack, even if it's strictly not 100% compatible, by tweaking a few downstream depenencies.

- Running the resulting bundle is tricky. The resulting bundle includes the JS parts of Node. This overlaps with what Node.js adds at runtime so it runs it twice. The ideal is actually to build a custom distribution of Node.js but this is generally overkill for what people want.

- Bindings change a lot. While Node.js's API notoriously doesn't change much. The internals do change a lot. By picking the API boundary in the middle of the internals of Node.js, it risks changing with any version. While technically observable changes, nobody else relies on these details. If this option was worth its weight, someone could probably maintain it but so far that has not been the case so we had to disable this option in CI to upgrade Node.

However, going forward I think there are alternative approaches we can explore.

- First class module system. This is something we really need at some point. A first class module system would be able to load Node.js module files from disk and package them up while excluding others. It doesn't have to be literally Node.js's module system. Close enough is ok. Especially as standards compliant ECMAScript modules get more popular. This lets us target compiling output that runs after Node's initialization.

- By introducing havocing and membranes in the boundaries, it becomes possible to initialize Node.js modules without actually knowing the internal of the boundaries.

- We've started optimizing residual functions which is much more interesting. However, this requires that code puts some constraints on how it works with its environment. It's not designed to be fully backwards compatible. That's probably a good thing but that also means that we can put constraints on the modules being Prepacked.

This removes the ability to prepack Prepack itself which is unfortunate but already wasn't being tested. To speed up Prepack itself, the [LLVM backend](https://github.com/facebook/prepack/pull/2264) seems much more useful if it can ever work on Prepack itself.
Pull Request resolved: https://github.com/facebook/prepack/pull/2267

Differential Revision: D8863788

Pulled By: sebmarkbage

fbshipit-source-id: d777ec9a95c8523b3386cfad553d9f691ec59074
2018-07-16 13:09:59 -07:00
Sebastian Markbage
51e495a4e2 Impure abstract getters on prototype chain (#2257)
Summary:
This fixes an issue where get on abstract top val didn't consider Receiver.

This also illustrates two common patterns of getters that are not pure. One is reading a mutable property known by Prepack and one is lazily initializing a shared mutable property.

I believe we'll want to continue supporting these patterns since they almost always work anyway. Note that supporting these don't add much negative consequence. We can still eliminate unused getters. Most of the time these patterns doesn't happen where the prototype is unknown or havoced.

Usually the getter is abstract because the Receiver itself is unknown or havoced (not its prototype). So havocing even more doesn't have any further negative downside.
Pull Request resolved: https://github.com/facebook/prepack/pull/2257

Differential Revision: D8862827

Pulled By: sebmarkbage

fbshipit-source-id: 7e6e98f8b85b6fceaa5131136cc6163d94f5d289
2018-07-16 12:58:27 -07:00
Herman Venter
7c6dddf236 Make CircleCI fail if not enough 262 tests pass (#2263)
Summary:
Release note: fix test262 to fail CircleCI test if not enough tests pass

The check for the number of tests that pass returned 1 to its caller, who just ignored it and then returned 0. Instead of that, now just call process.exit(1) when the check fails.

Also updated the expected number of ES6 tests that pass. It appears that updating Babel had a positive effect on those. Possibly it also causes one more ES5 test to fail when running locally (but not on Circle). That might be because of an ES5 test that now times out and an ES6 test that now does not. I have not investigated this as it seems of little importance right now.
Pull Request resolved: https://github.com/facebook/prepack/pull/2263

Reviewed By: trueadm

Differential Revision: D8859210

Pulled By: hermanventer

fbshipit-source-id: 724dcde05927cc914f6f9517f14dc230b8b0ad2e
2018-07-16 08:44:59 -07:00
Dominic Gannaway
c5f300de04 Re-enable traverse cache clear (#2260)
Summary:
Release notes: none

Follow up to https://github.com/facebook/prepack/pull/2256. This re-enables Babel traverse cache clearing, but with the Babel 7 API.
Pull Request resolved: https://github.com/facebook/prepack/pull/2260

Differential Revision: D8850784

Pulled By: trueadm

fbshipit-source-id: 9d61feff152400262a050d72b9ee3567ddcf11e2
2018-07-14 10:53:17 -07:00
Dominic Gannaway
e170c37aaa Upgrade Prepack to Babel 7 (#2256)
Summary:
Release notes: upgrades Prepack to use Babel 7.0.0-beta.53

This is a big PR that updates all of Prepack to Babel 7. Babylon is now `babel/parser` and pretty much all of the the previous Babel packages are now located in scoped packages. I had to make a bunch of changes around Jest/Flow/Webpack to get this all working. The build times of building Prepack itself seem considerably faster (easily twice as fast locally). I followed most of the Babel 6 -> 7 upgrade guide from the Babel site in terms of changing nodes and type definitions to match the new ones.
Pull Request resolved: https://github.com/facebook/prepack/pull/2256

Differential Revision: D8850583

Pulled By: trueadm

fbshipit-source-id: 2d2aaec25c6a1ccd1ec0c08c5e7e2a71f78ac2d8
2018-07-14 09:55:18 -07:00
Caleb Meredith
37d692480b Optimize ReactEquivalenceSet (#2243)
Summary:
I generated the following program (now added to test cases) and while I would expect its evaluation in Prepack to terminate reasonably quickly, I did not see the program terminate after five minutes of execution:

https://gist.github.com/calebmer/2bf1d84a5b7849fa732bce69811df10b

After this change that same program’s Prepack evaluation terminates in about two seconds. This change also saves about 2.8s on the evaluation of our internal bundle which is about 3% of the total time.

What was happening? Why did my program fail to terminate execution in five minutes? Why was the internal bundle relatively ok compared to my extreme case?

In my program, I would [expect the component `Mu`](https://gist.github.com/calebmer/2bf1d84a5b7849fa732bce69811df10b#file-program-js-L289-L291) to be inlined about 60 times. Which means there should only be about 60 calls to `ReactElementSet#add` for each of `Mu`’s `div`s. However, after some basic instrumentation I observed way over ten thousand visits to these React elements.

This pair of method calls happens a couple times in `ReactEquivalenceSet` and friends.

5f7256f17e/src/react/ReactEquivalenceSet.js (L233-L234)

Both `getEquivalentPropertyValue`…

5f7256f17e/src/react/ReactEquivalenceSet.js (L255)

…and `getValue`…

5f7256f17e/src/react/ReactEquivalenceSet.js (L104)

…end up calling `ReactElementSet#add` with the same React element. Then `ReactElementSet#add` ends up recursing into children. So the root element is added 2 times, so the root’s children are added 4 times each, so each child of those elements are added 8 times each, and so on. So if a leaf element is `n` deep in the tree it will be added `2^n` times. The most nested `Mu` child was 9 elements deep. `Mu` appeared 60 times at many levels of nesting.

I’m not entirely sure why my generated case was so much worse then our internal bundle. My two best guesses are: lots of repeated components or deeper nesting?

My fix was to move the `getValue` call into `getEquivalentPropertyValue`. This way if `getEquivalentPropertyValue` already finds an equivalent value we can short-circuit and not run `getValue` which will perform the same operation.
Pull Request resolved: https://github.com/facebook/prepack/pull/2243

Reviewed By: trueadm

Differential Revision: D8811463

Pulled By: calebmer

fbshipit-source-id: 4f30a75e96c6592456f5b21ee9c2ee3e40b56d81
2018-07-13 13:40:00 -07:00