diff --git a/src/intrinsics/ecma262/JSON.js b/src/intrinsics/ecma262/JSON.js index 602c89a23..af5c9d016 100644 --- a/src/intrinsics/ecma262/JSON.js +++ b/src/intrinsics/ecma262/JSON.js @@ -305,7 +305,7 @@ function InternalCloneObject(realm: Realm, val: ObjectValue): ObjectValue { } if (val.isPartialObject()) clone.makePartial(); 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; } diff --git a/src/serializer/Emitter.js b/src/serializer/Emitter.js index 3cd0f66ce..3431ed2cf 100644 --- a/src/serializer/Emitter.js +++ b/src/serializer/Emitter.js @@ -11,7 +11,6 @@ import { AbstractValue, - ArrayValue, BoundFunctionValue, FunctionValue, ObjectValue, @@ -38,9 +37,9 @@ type EmitterDependenciesVisitorCallbacks = { // 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. 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. - onArrayWithWidenedNumericProperty?: ArrayValue => void | T, + onIntrinsicDerivedObject?: ObjectValue => void | T, }; // 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._activeGeneratorStack = [this._mainBody]; this._finalized = false; - let mustWaitForValue = (val: AbstractValue | ArrayValue) => { + let mustWaitForValue = (val: AbstractValue | ObjectValue) => { if (this.cannotDeclare()) return false; if (this.hasBeenDeclared(val)) return false; let activeOptimizedFunction = this.getActiveOptimizedFunction(); @@ -95,7 +94,7 @@ export class Emitter { }, onAbstractValueWithIdentifier: val => derivedIds.has(val.getIdentifier()) && mustWaitForValue(val) ? val : undefined, - onArrayWithWidenedNumericProperty: val => (mustWaitForValue(val) ? val : undefined), + onIntrinsicDerivedObject: val => (mustWaitForValue(val) ? val : undefined), }; this._conditionalFeasibility = conditionalFeasibility; } @@ -351,10 +350,8 @@ export class Emitter { result = recurse(val.$Description); if (result !== undefined) return result; } - } else if (val instanceof ArrayValue && ArrayValue.isIntrinsicAndHasWidenedNumericProperty(val)) { - result = callbacks.onArrayWithWidenedNumericProperty - ? callbacks.onArrayWithWidenedNumericProperty(val) - : undefined; + } else if (val instanceof ObjectValue && ObjectValue.isIntrinsicDerivedObject(val)) { + result = callbacks.onIntrinsicDerivedObject ? callbacks.onIntrinsicDerivedObject(val) : undefined; if (result !== undefined) return result; } else if (val instanceof ObjectValue) { let kind = val.getKind(); diff --git a/src/serializer/ResidualHeapSerializer.js b/src/serializer/ResidualHeapSerializer.js index e7f21f39b..beeee3d9c 100644 --- a/src/serializer/ResidualHeapSerializer.js +++ b/src/serializer/ResidualHeapSerializer.js @@ -940,11 +940,9 @@ export class ResidualHeapSerializer { // which is related to one of the scopes this value is used by. let notYetDoneBodies = new Set(); this.emitter.dependenciesVisitor(val, { - onArrayWithWidenedNumericProperty: dependency => { + onIntrinsicDerivedObject: dependency => { if (trace) { - console.log( - ` depending on array with widened numeric properties and an identifier ${dependency.intrinsicName || "?"}` - ); + console.log(` depending on intrinsic derived object and an identifier ${dependency.intrinsicName || "?"}`); } invariant( optimizedFunctionRoot === undefined || !!this.emitter.getActiveOptimizedFunction(), diff --git a/src/serializer/ResidualHeapVisitor.js b/src/serializer/ResidualHeapVisitor.js index 88510e656..15321855a 100644 --- a/src/serializer/ResidualHeapVisitor.js +++ b/src/serializer/ResidualHeapVisitor.js @@ -1087,7 +1087,7 @@ export class ResidualHeapVisitor { } else if (val.isIntrinsic()) { // 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. - if (val instanceof ObjectValue && val._isScopedTemplate) { + if (val instanceof ObjectValue && val.isScopedTemplate) { this.preProcessValue(val); this.postProcessValue(val); } else diff --git a/src/utils/generator.js b/src/utils/generator.js index 53b28a44f..251c0f23e 100644 --- a/src/utils/generator.js +++ b/src/utils/generator.js @@ -1067,7 +1067,7 @@ export class Generator { let id = this.preludeGenerator.nameGenerator.generate("derived"); let value = buildValue(id); 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); this._addDerivedEntry({ isPure: optionalArgs ? optionalArgs.isPure : undefined, diff --git a/src/values/ArrayValue.js b/src/values/ArrayValue.js index 72b6a76c4..61d8fd6cf 100644 --- a/src/values/ArrayValue.js +++ b/src/values/ArrayValue.js @@ -242,7 +242,8 @@ export default class ArrayValue extends ObjectValue { } 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; if (prop !== undefined && prop.descriptor !== undefined) { const desc = prop.descriptor.throwIfNotConcrete(obj.$Realm); diff --git a/src/values/ObjectValue.js b/src/values/ObjectValue.js index c3222d7d8..9953c06ff 100644 --- a/src/values/ObjectValue.js +++ b/src/values/ObjectValue.js @@ -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 // 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 _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; return false; } + + static isIntrinsicDerivedObject(obj: Value): boolean { + return obj instanceof ObjectValue && obj.intrinsicName !== undefined && obj.isScopedTemplate !== undefined; + } }