Unify logic for checking for intrinsic objects that have been derived (#2581)

Summary:
Release notes: none

This PR cleans up some of the `onArrayWithWidenedNumericProperty` logic, making it more generic for cases where we want to use concrete derived object values. Furthermore, it strengthens the validation process by checking for existence of `isScopedTemplate` on the internal object.
Pull Request resolved: https://github.com/facebook/prepack/pull/2581

Differential Revision: D10141954

Pulled By: trueadm

fbshipit-source-id: 8de828080b8a41357830cdbf7e49359512bc7244
This commit is contained in:
Dominic Gannaway 2018-10-02 03:27:13 -07:00 committed by Facebook Github Bot
parent ed784d6899
commit 5fc6feeeb2
7 changed files with 18 additions and 18 deletions

View File

@ -305,7 +305,7 @@ function InternalCloneObject(realm: Realm, val: ObjectValue): ObjectValue {
} }
if (val.isPartialObject()) clone.makePartial(); if (val.isPartialObject()) clone.makePartial();
if (val.isSimpleObject()) clone.makeSimple(); if (val.isSimpleObject()) clone.makeSimple();
clone._isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope clone.isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope
return clone; return clone;
} }

View File

@ -11,7 +11,6 @@
import { import {
AbstractValue, AbstractValue,
ArrayValue,
BoundFunctionValue, BoundFunctionValue,
FunctionValue, FunctionValue,
ObjectValue, ObjectValue,
@ -38,9 +37,9 @@ type EmitterDependenciesVisitorCallbacks<T> = {
// Callback invoked whenever a dependency is visited that is an abstract value with an identifier. // Callback invoked whenever a dependency is visited that is an abstract value with an identifier.
// A return value that is not undefined indicates that the visitor should stop, and return the value as the overall result. // A return value that is not undefined indicates that the visitor should stop, and return the value as the overall result.
onAbstractValueWithIdentifier?: AbstractValue => void | T, onAbstractValueWithIdentifier?: AbstractValue => void | T,
// Callback invoked whenever a dependency is visited that is an array value with a widened numeric property. // Callback invoked whenever a dependency is visited that is an intrinsic object that was derived
// A return value that is not undefined indicates that the visitor should stop, and return the value as the overall result. // A return value that is not undefined indicates that the visitor should stop, and return the value as the overall result.
onArrayWithWidenedNumericProperty?: ArrayValue => void | T, onIntrinsicDerivedObject?: ObjectValue => void | T,
}; };
// The emitter keeps track of a stack of what's currently being emitted. // The emitter keeps track of a stack of what's currently being emitted.
@ -78,7 +77,7 @@ export class Emitter {
this._activeValues = new Set(); this._activeValues = new Set();
this._activeGeneratorStack = [this._mainBody]; this._activeGeneratorStack = [this._mainBody];
this._finalized = false; this._finalized = false;
let mustWaitForValue = (val: AbstractValue | ArrayValue) => { let mustWaitForValue = (val: AbstractValue | ObjectValue) => {
if (this.cannotDeclare()) return false; if (this.cannotDeclare()) return false;
if (this.hasBeenDeclared(val)) return false; if (this.hasBeenDeclared(val)) return false;
let activeOptimizedFunction = this.getActiveOptimizedFunction(); let activeOptimizedFunction = this.getActiveOptimizedFunction();
@ -95,7 +94,7 @@ export class Emitter {
}, },
onAbstractValueWithIdentifier: val => onAbstractValueWithIdentifier: val =>
derivedIds.has(val.getIdentifier()) && mustWaitForValue(val) ? val : undefined, derivedIds.has(val.getIdentifier()) && mustWaitForValue(val) ? val : undefined,
onArrayWithWidenedNumericProperty: val => (mustWaitForValue(val) ? val : undefined), onIntrinsicDerivedObject: val => (mustWaitForValue(val) ? val : undefined),
}; };
this._conditionalFeasibility = conditionalFeasibility; this._conditionalFeasibility = conditionalFeasibility;
} }
@ -351,10 +350,8 @@ export class Emitter {
result = recurse(val.$Description); result = recurse(val.$Description);
if (result !== undefined) return result; if (result !== undefined) return result;
} }
} else if (val instanceof ArrayValue && ArrayValue.isIntrinsicAndHasWidenedNumericProperty(val)) { } else if (val instanceof ObjectValue && ObjectValue.isIntrinsicDerivedObject(val)) {
result = callbacks.onArrayWithWidenedNumericProperty result = callbacks.onIntrinsicDerivedObject ? callbacks.onIntrinsicDerivedObject(val) : undefined;
? callbacks.onArrayWithWidenedNumericProperty(val)
: undefined;
if (result !== undefined) return result; if (result !== undefined) return result;
} else if (val instanceof ObjectValue) { } else if (val instanceof ObjectValue) {
let kind = val.getKind(); let kind = val.getKind();

View File

@ -940,11 +940,9 @@ export class ResidualHeapSerializer {
// which is related to one of the scopes this value is used by. // which is related to one of the scopes this value is used by.
let notYetDoneBodies = new Set(); let notYetDoneBodies = new Set();
this.emitter.dependenciesVisitor(val, { this.emitter.dependenciesVisitor(val, {
onArrayWithWidenedNumericProperty: dependency => { onIntrinsicDerivedObject: dependency => {
if (trace) { if (trace) {
console.log( console.log(` depending on intrinsic derived object and an identifier ${dependency.intrinsicName || "?"}`);
` depending on array with widened numeric properties and an identifier ${dependency.intrinsicName || "?"}`
);
} }
invariant( invariant(
optimizedFunctionRoot === undefined || !!this.emitter.getActiveOptimizedFunction(), optimizedFunctionRoot === undefined || !!this.emitter.getActiveOptimizedFunction(),

View File

@ -1087,7 +1087,7 @@ export class ResidualHeapVisitor {
} else if (val.isIntrinsic()) { } else if (val.isIntrinsic()) {
// All intrinsic values exist from the beginning of time... // All intrinsic values exist from the beginning of time...
// ...except for a few that come into existence as templates for abstract objects via executable code. // ...except for a few that come into existence as templates for abstract objects via executable code.
if (val instanceof ObjectValue && val._isScopedTemplate) { if (val instanceof ObjectValue && val.isScopedTemplate) {
this.preProcessValue(val); this.preProcessValue(val);
this.postProcessValue(val); this.postProcessValue(val);
} else } else

View File

@ -1067,7 +1067,7 @@ export class Generator {
let id = this.preludeGenerator.nameGenerator.generate("derived"); let id = this.preludeGenerator.nameGenerator.generate("derived");
let value = buildValue(id); let value = buildValue(id);
value.intrinsicNameGenerated = true; value.intrinsicNameGenerated = true;
value._isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope value.isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope
invariant(value.intrinsicName === id); invariant(value.intrinsicName === id);
this._addDerivedEntry({ this._addDerivedEntry({
isPure: optionalArgs ? optionalArgs.isPure : undefined, isPure: optionalArgs ? optionalArgs.isPure : undefined,

View File

@ -242,7 +242,8 @@ export default class ArrayValue extends ObjectValue {
} }
static isIntrinsicAndHasWidenedNumericProperty(obj: Value): boolean { static isIntrinsicAndHasWidenedNumericProperty(obj: Value): boolean {
if (obj instanceof ArrayValue && obj.intrinsicName !== undefined) { if (obj instanceof ArrayValue && obj.intrinsicName !== undefined && obj.isScopedTemplate !== undefined) {
invariant(ObjectValue.isIntrinsicDerivedObject(obj));
const prop = obj.unknownProperty; const prop = obj.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined) { if (prop !== undefined && prop.descriptor !== undefined) {
const desc = prop.descriptor.throwIfNotConcrete(obj.$Realm); const desc = prop.descriptor.throwIfNotConcrete(obj.$Realm);

View File

@ -279,7 +279,7 @@ export default class ObjectValue extends ConcreteValue {
// Specifies whether the object is a template that needs to be created in a scope // Specifies whether the object is a template that needs to be created in a scope
// If set, this happened during object initialization and the value is never changed again, so not tracked. // If set, this happened during object initialization and the value is never changed again, so not tracked.
_isScopedTemplate: void | true; isScopedTemplate: void | true;
// If true, then unknown properties should return transitively simple abstract object values // If true, then unknown properties should return transitively simple abstract object values
_simplicityIsTransitive: AbstractValue | BooleanValue; _simplicityIsTransitive: AbstractValue | BooleanValue;
@ -718,4 +718,8 @@ export default class ObjectValue extends ConcreteValue {
if (pb.internalSlot && typeof pb.key === "string" && pb.key[0] === "_") return true; if (pb.internalSlot && typeof pb.key === "string" && pb.key[0] === "_") return true;
return false; return false;
} }
static isIntrinsicDerivedObject(obj: Value): boolean {
return obj instanceof ObjectValue && obj.intrinsicName !== undefined && obj.isScopedTemplate !== undefined;
}
} }