Improve fb-www mocks objectWithoutProperties value by ensuring we store known values (#2194)

Summary:
Release notes: none

When the `objectWithoutProperties` mock was originally created, my knowledge of Prepack's internals wasn't as good as it was now. Now that I understand how AbstractObjectValues work, we can safely add the known values in `objectWithoutProperties` to the abstract backing object. The backing object was missing these values before and was an empty empty that was partial. This should give more data and value on our internal bundle, where before the values would be lost unnecessarily.
Closes https://github.com/facebook/prepack/pull/2194

Differential Revision: D8716289

Pulled By: trueadm

fbshipit-source-id: 451065473ea09943831f75c0bc15490e73c8d947
This commit is contained in:
Dominic Gannaway 2018-07-02 13:03:07 -07:00 committed by Facebook Github Bot
parent 5159b0d832
commit 67a47fd48f
7 changed files with 21768 additions and 22 deletions

File diff suppressed because it is too large Load Diff

1171
scripts/test-react.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@
/* @flow */
import type { Realm } from "../../realm.js";
import { ValuesDomain } from "../../domains/index.js";
import {
ArrayValue,
AbstractValue,
@ -81,6 +82,25 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
});
inheritsValue.intrinsicName = `babelHelpers.inherits`;
const createObjectWithoutProperties = (obj: ObjectValue, keys: ArrayValue) => {
let removeKeys = new Set();
forEachArrayValue(realm, keys, key => {
if (key instanceof StringValue || key instanceof NumberValue) {
removeKeys.add(key.value);
}
});
let newObject = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
for (let [propName, binding] of obj.properties) {
if (!removeKeys.has(propName)) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
let value = Get(realm, obj, propName);
Properties.Set(realm, newObject, propName, value, true);
}
}
}
return newObject;
};
//babelHelpers.objectWithoutProperties
let objectWithoutPropertiesValue = new NativeFunctionValue(
realm,
@ -90,38 +110,33 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
(context, [obj, keys]) => {
invariant(obj instanceof ObjectValue || obj instanceof AbstractObjectValue || obj instanceof AbstractValue);
invariant(keys instanceof ArrayValue);
if (obj.isPartialObject() || obj instanceof AbstractObjectValue || obj instanceof AbstractValue) {
if (obj.mightBeObject() && ((obj instanceof AbstractValue && obj.values.isTop()) || obj.isPartialObject())) {
let temporalArgs = [objectWithoutPropertiesValue, obj, keys];
let temporalConfig = { skipInvariant: true, isPure: true };
let value = AbstractValue.createTemporalFromBuildFunction(
realm,
ObjectValue,
[objectWithoutPropertiesValue, obj, keys],
temporalArgs,
([methodNode, objNode, propRemoveNode]) => {
return t.callExpression(methodNode, [objNode, propRemoveNode]);
},
{ skipInvariant: true, isPure: true }
temporalConfig
);
if (value instanceof AbstractObjectValue) {
// as we are returning an abstract object, we mark it as simple
value.makeSimple();
invariant(value instanceof AbstractObjectValue);
if (obj instanceof ObjectValue) {
let template = createObjectWithoutProperties(obj, keys);
value.values = new ValuesDomain(template);
}
// Store the args for the temporal so we can easily clone
// and reconstruct the temporal at another point, rather than
// mutate the existing temporal
realm.temporalAliasArgs.set(value, temporalArgs);
// as we are returning an abstract object, we mark it as simple
value.makeSimple();
return value;
} else {
let removeKeys = new Set();
forEachArrayValue(realm, keys, key => {
if (key instanceof StringValue || key instanceof NumberValue) {
removeKeys.add(key.value);
}
});
let newObject = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
for (let [propName, binding] of obj.properties) {
if (!removeKeys.has(propName)) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
let value = Get(realm, obj, propName);
Properties.Set(realm, newObject, propName, value, true);
}
}
}
return newObject;
invariant(obj instanceof ObjectValue);
return createObjectWithoutProperties(obj, keys);
}
}
);

View File

@ -127,6 +127,14 @@ it("fb-www 23", () => {
runTest(__dirname + "/FBMocks/fb23.js");
});
it("fb-www 24", () => {
runTest(__dirname + "/FBMocks/fb24.js");
});
it("fb-www 25", () => {
runTest(__dirname + "/FBMocks/fb25.js");
});
it("repl example", () => {
runTest(__dirname + "/FBMocks/repl-example.js");
});

View File

@ -0,0 +1,59 @@
var React = require("react");
// FB www polyfill
if (!this.babelHelpers) {
this.babelHelpers = {
inherits(subClass, superClass) {
Object.assign(subClass, superClass);
subClass.prototype = Object.create(superClass && superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__superConstructor__ = superClass;
return superClass;
},
_extends: Object.assign,
extends: Object.assign,
objectWithoutProperties(obj, keys) {
var target = {};
var hasOwn = Object.prototype.hasOwnProperty;
for (var i in obj) {
if (!hasOwn.call(obj, i) || keys.indexOf(i) >= 0) {
continue;
}
target[i] = obj[i];
}
return target;
},
taggedTemplateLiteralLoose(strings, raw) {
strings.raw = raw;
return strings;
},
bind: Function.prototype.bind,
};
}
function App(props) {
var data = {};
var someProps = Object.assign(data, props, {
text: "Text!",
})
var propsWithout = babelHelpers.objectWithoutProperties(data, []);
return <div>{propsWithout.text}</div>
}
App.getTrials = function(renderer, Root, data, isCompiled) {
if (isCompiled) {
var a = App({});
var b = App({});
if (a !== b) {
throw new Error("The values should be the same!");
}
}
renderer.update(<Root />);
return [["fb24", renderer.toJSON()]];
};
if (this.__optimizeReactComponentTree) {
__optimizeReactComponentTree(App);
}
module.exports = App;

View File

@ -0,0 +1,58 @@
var React = require("react");
// FB www polyfill
if (!this.babelHelpers) {
this.babelHelpers = {
inherits(subClass, superClass) {
Object.assign(subClass, superClass);
subClass.prototype = Object.create(superClass && superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__superConstructor__ = superClass;
return superClass;
},
_extends: Object.assign,
extends: Object.assign,
objectWithoutProperties(obj, keys) {
var target = {};
var hasOwn = Object.prototype.hasOwnProperty;
for (var i in obj) {
if (!hasOwn.call(obj, i) || keys.indexOf(i) >= 0) {
continue;
}
target[i] = obj[i];
}
return target;
},
taggedTemplateLiteralLoose(strings, raw) {
strings.raw = raw;
return strings;
},
bind: Function.prototype.bind,
};
}
function App(props) {
var someProps = Object.assign({}, props, {
text: "Text!",
})
var propsWithout = babelHelpers.objectWithoutProperties(someProps, []);
return <div>{propsWithout.text}</div>
}
App.getTrials = function(renderer, Root, data, isCompiled) {
if (isCompiled) {
var a = App({});
var b = App({});
if (a !== b) {
throw new Error("The values should be the same!");
}
}
renderer.update(<Root />);
return [["fb25", renderer.toJSON()]];
};
if (this.__optimizeReactComponentTree) {
__optimizeReactComponentTree(App);
}
module.exports = App;

View File

@ -2396,6 +2396,142 @@ ReactStatistics {
}
`;
exports[`fb-www 24: (JSX => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 24: (JSX => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 24: (createElement => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 24: (createElement => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 25: (JSX => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 25: (JSX => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 25: (createElement => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www 25: (createElement => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,
"evaluatedRootNodes": Array [
Object {
"children": Array [],
"message": "",
"name": "App",
"status": "ROOT",
},
],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 1,
}
`;
exports[`fb-www: (JSX => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 1,