Use isPure and isInvariant in many temporals (#2097)

Summary:
Release notes: none

When we create temporals that are pure, let's mark them as pure and also skip creating invariants for them too. Making these changes allows the serializer to not emit temporals that never end up getting used, reducing the output and also making it easier to debug the output (less spam of temporals).
Closes https://github.com/facebook/prepack/pull/2097

Differential Revision: D8329043

Pulled By: trueadm

fbshipit-source-id: f1ef4185793115dd7982b8162a7e3dc4038a15f9
This commit is contained in:
Dominic Gannaway 2018-06-12 18:09:24 -07:00 committed by Facebook Github Bot
parent 5ee7ba4aa8
commit 9c11ec9a5e
11 changed files with 137 additions and 53 deletions

View File

@ -97,7 +97,8 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
[objectWithoutPropertiesValue, obj, keys],
([methodNode, objNode, propRemoveNode]) => {
return t.callExpression(methodNode, [objNode, propRemoveNode]);
}
},
{ skipInvariant: true, isPure: true }
);
if (value instanceof AbstractObjectValue) {
// as we are returning an abstract object, we mark it as simple
@ -189,8 +190,12 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
function createMagicGlobalFunction(realm: Realm, global: ObjectValue | AbstractObjectValue, functionName: string) {
global.$DefineOwnProperty(functionName, {
value: new NativeFunctionValue(realm, functionName, functionName, 0, (context, args) => {
let val = AbstractValue.createTemporalFromBuildFunction(realm, FunctionValue, args, _args =>
t.callExpression(t.identifier(functionName), ((_args: any): Array<any>))
let val = AbstractValue.createTemporalFromBuildFunction(
realm,
FunctionValue,
args,
_args => t.callExpression(t.identifier(functionName), ((_args: any): Array<any>)),
{ skipInvariant: true, isPure: true }
);
invariant(val instanceof AbstractValue);
return val;
@ -218,11 +223,16 @@ function createBootloader(realm: Realm, global: ObjectValue | AbstractObjectValu
let loadModules = new NativeFunctionValue(realm, "loadModules", "loadModules", 1, (context, args) => {
invariant(context.$Realm.generator);
let val = AbstractValue.createTemporalFromBuildFunction(realm, FunctionValue, args, _args =>
t.callExpression(
t.memberExpression(t.identifier("Bootloader"), t.identifier("loadModules")),
((_args: any): Array<any>)
)
let val = AbstractValue.createTemporalFromBuildFunction(
realm,
FunctionValue,
args,
_args =>
t.callExpression(
t.memberExpression(t.identifier("Bootloader"), t.identifier("loadModules")),
((_args: any): Array<any>)
),
{ skipInvariant: true }
);
invariant(val instanceof AbstractValue);
return val;

View File

@ -536,16 +536,19 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
realm,
ObjectValue,
[funcValue, defaultValue],
([methodNode, defaultValueNode]) => {
return t.callExpression(methodNode, [defaultValueNode]);
}
([methodNode, defaultValueNode]) => t.callExpression(methodNode, [defaultValueNode]),
{ skipInvariant: true, isPure: true }
);
invariant(consumer instanceof AbstractObjectValue);
consumer.values = new ValuesDomain(new Set([consumerObject]));
let provider = AbstractValue.createTemporalFromBuildFunction(realm, ObjectValue, [consumer], ([consumerNode]) => {
return t.memberExpression(consumerNode, t.identifier("Provider"));
});
let provider = AbstractValue.createTemporalFromBuildFunction(
realm,
ObjectValue,
[consumer],
([consumerNode]) => t.memberExpression(consumerNode, t.identifier("Provider")),
{ skipInvariant: true, isPure: true }
);
invariant(provider instanceof AbstractObjectValue);
provider.values = new ValuesDomain(new Set([providerObject]));
@ -570,7 +573,8 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
[funcVal],
([createRefNode]) => {
return t.callExpression(createRefNode, []);
}
},
{ skipInvariant: true, isPure: true }
);
invariant(createRef instanceof AbstractObjectValue);
return createRef;
@ -583,7 +587,8 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
[funcVal, func],
([forwardRefNode, funcNode]) => {
return t.callExpression(forwardRefNode, [funcNode]);
}
},
{ skipInvariant: true, isPure: true }
);
invariant(forwardedRef instanceof AbstractObjectValue);
realm.react.abstractHints.set(
@ -611,7 +616,8 @@ export function createMockReactDOM(realm: Realm, reactDomRequireName: string): O
[funcVal, ...args],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);
return reactDomMethod;
@ -629,7 +635,8 @@ export function createMockReactDOM(realm: Realm, reactDomRequireName: string): O
[funcVal, reactPortalValue, domNodeValue],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);
realm.react.abstractHints.set(
@ -659,7 +666,8 @@ export function createMockReactDOMServer(realm: Realm, requireName: string): Obj
[funcVal, ...args],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);
return reactDomMethod;

View File

@ -109,14 +109,20 @@ function createReactRelayContainer(
// allowing us to reconstruct this ReactReact.createSomeContainer(...) again
// we also pass a reactHint so the reconciler can properly deal with this
addMockFunctionToObject(realm, reactRelay, relayRequireName, containerName, (funcValue, args) => {
let value = AbstractValue.createTemporalFromBuildFunction(realm, FunctionValue, [reactRelay, ...args], _args => {
let [reactRelayIdent, ...otherArgs] = _args;
let value = AbstractValue.createTemporalFromBuildFunction(
realm,
FunctionValue,
[reactRelay, ...args],
_args => {
let [reactRelayIdent, ...otherArgs] = _args;
return t.callExpression(
t.memberExpression(reactRelayIdent, t.identifier(containerName)),
((otherArgs: any): Array<any>)
);
});
return t.callExpression(
t.memberExpression(reactRelayIdent, t.identifier(containerName)),
((otherArgs: any): Array<any>)
);
},
{ skipInvariant: true, isPure: true }
);
invariant(value instanceof AbstractValue);
let firstRenderContainerValue = Get(realm, reactRelayFirstRenderValue, containerName);
let firstRenderValue = realm.intrinsics.undefined;

View File

@ -559,8 +559,12 @@ export function GetFromArrayWithWidenedNumericProperty(realm: Realm, arr: ArrayV
invariant(proto instanceof ObjectValue && proto === realm.intrinsics.ArrayPrototype);
if (typeof P === "string") {
if (P === "length") {
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, [arr], ([o]) =>
t.memberExpression(o, t.identifier("length"), false)
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
[arr],
([o]) => t.memberExpression(o, t.identifier("length"), false),
{ skipInvariant: true, isPure: true }
);
}
let prototypeBinding = proto.properties.get(P);
@ -573,7 +577,14 @@ export function GetFromArrayWithWidenedNumericProperty(realm: Realm, arr: ArrayV
}
}
let prop = typeof P === "string" ? new StringValue(realm, P) : P;
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [arr, prop], ([o, p]) =>
t.memberExpression(o, p, true)
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[arr, prop],
([o, p]) => t.memberExpression(o, p, true),
{
skipInvariant: true,
isPure: true,
}
);
}

View File

@ -1177,8 +1177,12 @@ export class PropertiesImplementation {
invariant(realm.generator);
let pname = realm.generator.getAsPropertyNameExpression(StringKey(P));
let absVal = AbstractValue.createTemporalFromBuildFunction(realm, Value, [O._templateFor || O], ([node]) =>
t.memberExpression(node, pname, !t.isIdentifier(pname))
let absVal = AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O._templateFor || O],
([node]) => t.memberExpression(node, pname, !t.isIdentifier(pname)),
{ isPure: true }
);
// TODO: We can't be sure what the descriptor will be, but the value will be abstract.
return { configurable: true, enumerable: true, value: absVal, writable: true };
@ -1219,7 +1223,7 @@ export class PropertiesImplementation {
([node]) => {
return t.memberExpression(node, pname, !t.isIdentifier(pname));
},
{ skipInvariant: true }
{ skipInvariant: true, isPure: true }
);
}
}

View File

@ -372,7 +372,8 @@ export function applyGetDerivedStateFromProps(
[objectAssign, newState, prevState, state],
([methodNode, ..._args]) => {
return t.callExpression(methodNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true }
);
newState.makeSimple();
newState.makePartial();
@ -392,7 +393,8 @@ export function applyGetDerivedStateFromProps(
[objectAssign, newState, prevState, state],
([methodNode, ..._args]) => {
return t.callExpression(methodNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true }
);
newState.makeSimple();
newState.makePartial();

View File

@ -880,7 +880,8 @@ export class Reconciler {
[reactDomPortalFunc, resolvedReactPortalValue, domNodeValue],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
}
},
{ skipInvariant: true, isPure: true }
);
}
return createPortalNode;

View File

@ -512,6 +512,7 @@ export default class AbstractObjectValue extends AbstractValue {
},
{
skipInvariant: true,
isPure: true,
}
);
};
@ -606,8 +607,12 @@ export default class AbstractObjectValue extends AbstractValue {
if (!(P instanceof AbstractValue)) return this.$Get(P, Receiver);
if (this.values.isTop()) {
if (this.isSimpleObject() && this.isIntrinsic()) {
return AbstractValue.createTemporalFromBuildFunction(this.$Realm, Value, [this, P], ([o, p]) =>
t.memberExpression(o, p, true)
return AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
Value,
[this, P],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
}
if (this.$Realm.isInPureScope()) {
@ -622,8 +627,12 @@ export default class AbstractObjectValue extends AbstractValue {
Havoc.value(this.$Realm, Receiver);
// Coercion can only have effects on anything reachable from the key.
Havoc.value(this.$Realm, P);
return AbstractValue.createTemporalFromBuildFunction(this.$Realm, Value, [Receiver, P], ([o, p]) =>
t.memberExpression(o, p, true)
return AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
Value,
[Receiver, P],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
}
let error = new CompilerDiagnostic(

View File

@ -562,8 +562,8 @@ export default class ObjectValue extends ConcreteValue {
let template = new ObjectValue(this.$Realm, this.$Realm.intrinsics.ObjectPrototype);
this.copyKeys(this.$OwnPropertyKeys(), this, template);
let result = AbstractValue.createTemporalFromBuildFunction(this.$Realm, ObjectValue, [template], ([x]) => x, {
isPure: true,
skipInvariant: true,
isPure: true,
});
invariant(result instanceof AbstractObjectValue);
result.values = new ValuesDomain(template);
@ -755,8 +755,12 @@ export default class ObjectValue extends ConcreteValue {
// this name on other parts of the prototype chain.
// TODO #1675: A fix to 1675 needs to take this into account.
Havoc.value(this.$Realm, Receiver);
return AbstractValue.createTemporalFromBuildFunction(this.$Realm, Value, [Receiver, P], ([o, p]) =>
t.memberExpression(o, p, true)
return AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
Value,
[Receiver, P],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
} else {
let error = new CompilerDiagnostic(
@ -779,14 +783,22 @@ export default class ObjectValue extends ConcreteValue {
if (this.isPartialObject()) {
if (isWidenedValue(P)) {
// TODO #1678: Use a snapshot or havoc this object.
return AbstractValue.createTemporalFromBuildFunction(this.$Realm, Value, [this, P], ([o, p]) =>
t.memberExpression(o, p, true)
return AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
Value,
[this, P],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
}
result = AbstractValue.createFromType(this.$Realm, Value, "sentinel member expression", [this, P]);
} else {
result = AbstractValue.createTemporalFromBuildFunction(this.$Realm, Value, [this, P], ([o, p]) =>
t.memberExpression(o, p, true)
result = AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
Value,
[this, P],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
}
@ -830,9 +842,13 @@ export default class ObjectValue extends ConcreteValue {
let pNumber = +pName;
if (pName === pNumber + "") propName = new NumberValue(this.$Realm, pNumber);
}
return AbstractValue.createTemporalFromBuildFunction(this.$Realm, absVal.getType(), [ob, propName], ([o, p]) => {
return t.memberExpression(o, p, true);
});
return AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
absVal.getType(),
[ob, propName],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
}
invariant(absVal.args.length === 3 && absVal.kind === "conditional");
let generic_cond = absVal.args[0];
@ -844,8 +860,12 @@ export default class ObjectValue extends ConcreteValue {
if (arg2 instanceof AbstractValue) {
if (arg2.kind === "template for prototype member expression") {
let ob = arg2.args[0];
arg2 = AbstractValue.createTemporalFromBuildFunction(this.$Realm, absVal.getType(), [ob, propName], ([o, p]) =>
t.memberExpression(o, p, true)
arg2 = AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
absVal.getType(),
[ob, propName],
([o, p]) => t.memberExpression(o, p, true),
{ skipInvariant: true, isPure: true }
);
} else if (arg2.args.length === 3) {
arg2 = this.specializeJoin(arg2, propName);

View File

@ -0,0 +1,13 @@
// does not contain:x.thisShouldNotAppear
function fn(x) {
var a = x.thisShouldNotAppear;
return 10;
}
global.__optimize && __optimize(fn);
inspect = function() {
return fn({});
};

View File

@ -502,7 +502,7 @@ global.result = __evaluatePureFunction(() => {
global.__optimize && __optimize(Main);
global.inspect = function() {
return Main({});
return Main({}, {});
};
return Main;