Re-land typed descriptors and printer (#2511)

Summary:
Release notes: landing two previously reverted PRs

I found the issue.

Flow doesn't support optional fields in classes. This effectively becomes enforced with Babel 7 since it treats the type annotations as field initializers. Which means that these fields always gets created.

We happened to only use this newer plugin for class fields internally which broke our builds only there.

2b4546d Use `undefined` instead of missing to represent absent field in descriptors. Fixed all the callsites that checks for `hasOwnProperty` or `in` that I could find.

922d40c Fixes a Flow issue in the text printer.

I filed a [follow up issue for ObjectValue](https://github.com/facebook/prepack/issues/2510) since it has the same problem.
Pull Request resolved: https://github.com/facebook/prepack/pull/2511

Reviewed By: trueadm

Differential Revision: D9569949

Pulled By: sebmarkbage

fbshipit-source-id: f8bf84c4385de4f0ff6bcd45badacd3b8c88c533
This commit is contained in:
Sebastian Markbage 2018-08-31 05:52:39 -07:00 committed by Facebook Github Bot
parent 69699c2d39
commit 54be9b535c
77 changed files with 3110 additions and 1474 deletions

View File

@ -59,7 +59,7 @@ jobs:
yarn test-react
yarn test-sourcemaps
yarn test-std-in
yarn test-test262 --expectedCounts 11944,5641,0 --statusFile ~/artifacts/test262-status.txt --timeout 120 --cpuScale 0.25 --verbose
yarn test-test262 --expectedCounts 11944,5641,0 --statusFile ~/artifacts/test262-status.txt --timeout 140 --cpuScale 0.25 --verbose
#yarn test-test262-new --statusFile ~/artifacts/test262-new-status.txt --timeout 120 --verbose
- store_artifacts:
path: ~/artifacts/

View File

@ -47,6 +47,7 @@ let execSpec;
let JSONTokenizer = require("../lib/utils/JSONTokenizer.js").default;
let { Linter } = require("eslint");
let lintConfig = require("./lint-config");
let TextPrinter = require("../lib/utils/TextPrinter.js").TextPrinter;
function transformWithBabel(code, plugins, presets) {
return babel.transform(code, {
@ -431,6 +432,7 @@ function runTest(name, code, options: PrepackOptions, args) {
return Promise.resolve(false);
} else {
let codeIterations = [];
let irIterations = [];
let markersToFind = [];
for (let [positive, marker] of [[true, "// does contain:"], [false, "// does not contain:"]]) {
for (let i = code.indexOf(marker); i >= 0; i = code.indexOf(marker, i + 1)) {
@ -507,6 +509,14 @@ function runTest(name, code, options: PrepackOptions, args) {
options.errorHandler = getErrorHandlerWithWarningCapture(diagnosticOutput, args.verbose);
}
let ir = "";
if (args.ir)
options.onExecute = (realm, optimizedFunctions) => {
new TextPrinter(line => {
ir += line + "\n";
}).print(realm, optimizedFunctions);
};
let serialized = prepackSources([{ filePath: name, fileContents: code, sourceMapContents: "" }], options);
if (serialized.statistics && serialized.statistics.delayedValues > 0) anyDelayedValues = true;
if (!serialized) {
@ -583,7 +593,11 @@ function runTest(name, code, options: PrepackOptions, args) {
if (!execSpec && options.lazyObjectsRuntime !== undefined) {
codeToRun = augmentCodeWithLazyObjectSupport(codeToRun, args.lazyObjectsRuntime);
}
if (args.verbose) console.log(codeToRun);
if (args.verbose) {
if (args.ir) console.log(`=== ir\n${ir}\n`);
console.log(`=== generated code\n${codeToRun}\n`);
}
irIterations.push(unescapeUniqueSuffix(ir, options.uniqueSuffix));
codeIterations.push(unescapeUniqueSuffix(codeToRun, options.uniqueSuffix));
if (args.es5) {
codeToRun = transformWithBabel(
@ -660,6 +674,10 @@ function runTest(name, code, options: PrepackOptions, args) {
console.error(chalk.underline("output of inspect() on original code"));
console.error(expected);
for (let ii = 0; ii < codeIterations.length; ii++) {
if (args.ir) {
console.error(chalk.underline(`ir in iteration ${ii}`));
console.error(irIterations[ii]);
}
console.error(chalk.underline(`generated code in iteration ${ii}`));
console.error(codeIterations[ii]);
}
@ -829,6 +847,7 @@ class ProgramArgs {
noLazySupport: boolean;
fast: boolean;
cpuprofilePath: string;
ir: boolean;
constructor(
debugNames: boolean,
debugScopes: boolean,
@ -839,7 +858,8 @@ class ProgramArgs {
lazyObjectsRuntime: string,
noLazySupport: boolean,
fast: boolean,
cpuProfilePath: string
cpuProfilePath: string,
ir: boolean
) {
this.debugNames = debugNames;
this.debugScopes = debugScopes;
@ -851,6 +871,7 @@ class ProgramArgs {
this.noLazySupport = noLazySupport;
this.fast = fast;
this.cpuprofilePath = cpuProfilePath;
this.ir = ir;
}
}
@ -885,7 +906,7 @@ function usage(): string {
return (
`Usage: ${process.argv[0]} ${process.argv[1]} ` +
EOL +
`[--debugNames] [--debugScopes] [--es5] [--fast] [--noLazySupport] [--verbose] [--filter <string>] [--outOfProcessRuntime <path>] `
`[--debugNames] [--debugScopes] [--es5] [--fast] [--noLazySupport] [--verbose] [--ir] [--filter <string>] [--outOfProcessRuntime <path>] `
);
}
@ -913,6 +934,7 @@ function argsParse(): ProgramArgs {
// to run tests. If not a separate node context used.
lazyObjectsRuntime: LAZY_OBJECTS_RUNTIME_NAME,
noLazySupport: false,
ir: false,
fast: false,
cpuprofilePath: "",
},
@ -949,6 +971,9 @@ function argsParse(): ProgramArgs {
if (typeof parsedArgs.cpuprofilePath !== "string") {
throw new ArgsParseError("cpuprofilePath must be a string");
}
if (typeof parsedArgs.ir !== "boolean") {
throw new ArgsParseError("ir must be a string");
}
let programArgs = new ProgramArgs(
parsedArgs.debugNames,
parsedArgs.debugScopes,
@ -959,7 +984,8 @@ function argsParse(): ProgramArgs {
parsedArgs.lazyObjectsRuntime,
parsedArgs.noLazySupport,
parsedArgs.fast,
parsedArgs.cpuprofilePath
parsedArgs.cpuprofilePath,
parsedArgs.ir
);
return programArgs;
}

136
src/descriptors.js Normal file
View File

@ -0,0 +1,136 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import invariant from "./invariant.js";
import type { AbstractValue, UndefinedValue, Value } from "./values/index.js";
import { CompilerDiagnostic, FatalError } from "./errors.js";
import type { CallableObjectValue } from "./types.js";
import type { Realm } from "./realm.js";
export class Descriptor {
constructor() {
invariant(this.constructor !== Descriptor, "Descriptor is an abstract base class");
}
throwIfNotConcrete(realm: Realm): PropertyDescriptor {
let error = new CompilerDiagnostic(
"only known descriptors supported",
realm.currentLocation,
"PP0042",
"FatalError"
);
realm.handleError(error);
throw new FatalError();
}
mightHaveBeenDeleted(): boolean {
invariant(false, "should have been overridden by subclass");
}
}
export type DescriptorInitializer = {|
writable?: boolean,
enumerable?: boolean,
configurable?: boolean,
value?: Value,
get?: UndefinedValue | CallableObjectValue | AbstractValue,
set?: UndefinedValue | CallableObjectValue | AbstractValue,
|};
// Normal descriptors are returned just like spec descriptors
export class PropertyDescriptor extends Descriptor {
writable: void | boolean;
enumerable: void | boolean;
configurable: void | boolean;
// If value instanceof EmptyValue, then this descriptor indicates that the
// corresponding property has been deleted.
value: void | Value;
get: void | UndefinedValue | CallableObjectValue | AbstractValue;
set: void | UndefinedValue | CallableObjectValue | AbstractValue;
constructor(desc: DescriptorInitializer | PropertyDescriptor) {
super();
this.writable = desc.writable;
this.enumerable = desc.enumerable;
this.configurable = desc.configurable;
this.value = desc.value;
this.get = desc.get;
this.set = desc.set;
}
throwIfNotConcrete(realm: Realm): PropertyDescriptor {
return this;
}
mightHaveBeenDeleted(): boolean {
if (this.value === undefined) return false;
return this.value.mightHaveBeenDeleted();
}
}
// Only internal properties (those starting with $ / where internalSlot of owning property binding is true) will ever have array values.
export class InternalSlotDescriptor extends Descriptor {
value: void | Value | Array<any>;
constructor(value?: void | Value | Array<any>) {
super();
this.value = Array.isArray(value) ? value.slice(0) : value;
}
mightHaveBeenDeleted(): boolean {
return false;
}
}
// Only used if the result of a join of two descriptors is not a data descriptor with identical attribute values.
// When present, any update to the property must produce effects that are the join of updating both descriptors,
// using joinCondition as the condition of the join.
export class AbstractJoinedDescriptor extends Descriptor {
joinCondition: AbstractValue;
// An undefined descriptor means it might be empty in this branch.
descriptor1: void | Descriptor;
descriptor2: void | Descriptor;
constructor(joinCondition: AbstractValue, descriptor1?: Descriptor, descriptor2?: Descriptor) {
super();
this.joinCondition = joinCondition;
this.descriptor1 = descriptor1;
this.descriptor2 = descriptor2;
}
mightHaveBeenDeleted(): boolean {
if (!this.descriptor1 || this.descriptor1.mightHaveBeenDeleted()) {
return true;
}
if (!this.descriptor2 || this.descriptor2.mightHaveBeenDeleted()) {
return true;
}
return false;
}
}
export function cloneDescriptor(d: void | PropertyDescriptor): void | PropertyDescriptor {
if (d === undefined) return undefined;
return new PropertyDescriptor(d);
}
// does not check if the contents of value properties are the same
export function equalDescriptors(d1: PropertyDescriptor, d2: PropertyDescriptor): boolean {
if (d1.writable !== d2.writable) return false;
if (d1.enumerable !== d2.enumerable) return false;
if (d1.configurable !== d2.configurable) return false;
if (d1.value !== undefined) {
if (d2.value === undefined) return false;
}
if (d1.get !== d2.get) return false;
if (d1.set !== d2.set) return false;
return true;
}

View File

@ -48,6 +48,7 @@ import PrimitiveValue from "./values/PrimitiveValue.js";
import { createOperationDescriptor } from "./utils/generator.js";
import { SourceMapConsumer, type NullableMappedPosition } from "source-map";
import { PropertyDescriptor } from "./descriptors.js";
function deriveGetBinding(realm: Realm, binding: Binding) {
let types = TypesDomain.topVal;
@ -448,12 +449,17 @@ export class ObjectEnvironmentRecord extends EnvironmentRecord {
// 4. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: configValue}).
return new BooleanValue(
realm,
Properties.DefinePropertyOrThrow(realm, bindings, N, {
value: realm.intrinsics.undefined,
writable: true,
enumerable: true,
configurable: configValue,
})
Properties.DefinePropertyOrThrow(
realm,
bindings,
N,
new PropertyDescriptor({
value: realm.intrinsics.undefined,
writable: true,
enumerable: true,
configurable: configValue,
})
)
);
}
@ -898,7 +904,8 @@ export class GlobalEnvironmentRecord extends EnvironmentRecord {
// 5. If existingProp is undefined, return false.
if (!existingProp) return false;
Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);
Properties.ThrowIfMightHaveBeenDeleted(existingProp);
existingProp = existingProp.throwIfNotConcrete(globalObject.$Realm);
// 6. If existingProp.[[Configurable]] is true, return false.
if (existingProp.configurable) return false;
@ -948,7 +955,8 @@ export class GlobalEnvironmentRecord extends EnvironmentRecord {
// 5. If existingProp is undefined, return ? IsExtensible(globalObject).
if (!existingProp) return IsExtensible(realm, globalObject);
Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);
Properties.ThrowIfMightHaveBeenDeleted(existingProp);
existingProp = existingProp.throwIfNotConcrete(globalObject.$Realm);
// 6. If existingProp.[[Configurable]] is true, return true.
if (existingProp.configurable) return true;
@ -1015,17 +1023,20 @@ export class GlobalEnvironmentRecord extends EnvironmentRecord {
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
let existingProp = globalObject.$GetOwnProperty(N);
if (existingProp) {
existingProp = existingProp.throwIfNotConcrete(globalObject.$Realm);
}
// 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then
let desc;
if (!existingProp || existingProp.configurable) {
// a. Let desc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D}.
desc = { value: V, writable: true, enumerable: true, configurable: D };
desc = new PropertyDescriptor({ value: V, writable: true, enumerable: true, configurable: D });
} else {
// 6. Else,
Properties.ThrowIfMightHaveBeenDeleted(existingProp.value);
Properties.ThrowIfMightHaveBeenDeleted(existingProp);
// a. Let desc be the PropertyDescriptor{[[Value]]: V }.
desc = { value: V };
desc = new PropertyDescriptor({ value: V });
}
// 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc).

View File

@ -155,7 +155,7 @@ function emitResidualLoopIfSafe(
if (key.object.unknownProperty === key) {
targetObject = key.object;
invariant(desc !== undefined);
let sourceValue = desc.value;
let sourceValue = desc.throwIfNotConcrete(realm).value;
if (sourceValue instanceof AbstractValue) {
// because sourceValue was written to key.object.unknownProperty it must be that
let cond = sourceValue.args[0];

View File

@ -16,6 +16,7 @@ import { MakeConstructor } from "../methods/construct.js";
import { Create, Functions, Properties } from "../singletons.js";
import { StringValue } from "../values/index.js";
import IsStrict from "../utils/strict.js";
import { PropertyDescriptor } from "../descriptors.js";
import type { BabelNodeFunctionDeclaration } from "@babel/types";
// ECMA262 14.1.20
@ -44,11 +45,16 @@ export default function(
let prototype = Create.ObjectCreate(realm, realm.intrinsics.GeneratorPrototype);
// 5. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, F, "prototype", {
value: prototype,
writable: true,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"prototype",
new PropertyDescriptor({
value: prototype,
writable: true,
configurable: false,
})
);
// 6. Perform SetFunctionName(F, name).
Functions.SetFunctionName(realm, F, name);

View File

@ -18,6 +18,7 @@ import { StringValue } from "../values/index.js";
import IsStrict from "../utils/strict.js";
import type { BabelNodeFunctionExpression } from "@babel/types";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default function(
ast: BabelNodeFunctionExpression,
@ -57,12 +58,17 @@ export default function(
prototype.originalConstructor = closure;
// 9. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, closure, "prototype", {
value: prototype,
writable: true,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
closure,
"prototype",
new PropertyDescriptor({
value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
// 10. Perform SetFunctionName(closure, name).
Functions.SetFunctionName(realm, closure, new StringValue(realm, name));
@ -126,12 +132,17 @@ export default function(
prototype.originalConstructor = closure;
// 5. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, closure, "prototype", {
value: prototype,
writable: true,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
closure,
"prototype",
new PropertyDescriptor({
value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
// 6. Return closure.
return closure;

View File

@ -195,10 +195,10 @@ function evaluateJSXChildren(
return array;
}
function isObjectEmpty(object: ObjectValue) {
function isObjectEmpty(realm: Realm, object: ObjectValue) {
let propertyCount = 0;
for (let [, binding] of object.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
if (binding && binding.descriptor && binding.descriptor.throwIfNotConcrete(realm).enumerable) {
propertyCount++;
}
}
@ -235,7 +235,7 @@ function evaluateJSXAttributes(
if (spreadValue instanceof ObjectValue && !spreadValue.isPartialObject()) {
for (let [spreadPropKey, binding] of spreadValue.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
if (binding && binding.descriptor && binding.descriptor.throwIfNotConcrete(realm).enumerable) {
setConfigProperty(spreadPropKey, Get(realm, spreadValue, spreadPropKey));
}
}
@ -249,7 +249,7 @@ function evaluateJSXAttributes(
if (hasNoPartialKeyOrRef(realm, spreadValue)) {
safeAbstractSpreadCount++;
}
if (!isObjectEmpty(config)) {
if (!isObjectEmpty(realm, config)) {
abstractPropsArgs.push(config);
}
abstractPropsArgs.push(spreadValue);

View File

@ -17,92 +17,117 @@ import initializeConsole from "../common/console.js";
import { FatalError } from "../../errors.js";
import invariant from "../../invariant.js";
import { TypesDomain, ValuesDomain } from "../../domains/index.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
if (!realm.isCompatibleWith(realm.MOBILE_JSC_VERSION) && !realm.isCompatibleWith("mobile"))
global.$DefineOwnProperty("console", {
value: initializeConsole(realm),
global.$DefineOwnProperty(
"console",
new PropertyDescriptor({
value: initializeConsole(realm),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty(
"self",
new PropertyDescriptor({
value: global,
writable: true,
enumerable: true,
configurable: true,
})
);
global.$DefineOwnProperty(
"window",
new PropertyDescriptor({
value: global,
writable: true,
enumerable: true,
configurable: true,
})
);
global.$DefineOwnProperty(
"document",
new PropertyDescriptor({
value: initializeDocument(realm),
writable: true,
enumerable: false,
configurable: true,
});
})
);
global.$DefineOwnProperty("self", {
value: global,
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty(
"setTimeout",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.setTimeout", "", 2, (context, args) => {
let callback = args[0].throwIfNotConcrete();
if (!(callback instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "callback arguments must be function");
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.setTimeout");
invariant(realm.generator !== undefined);
let generator = realm.generator;
return generator.emitCallAndCaptureResult(TypesDomain.topVal, ValuesDomain.topVal, "global.setTimeout", args);
}),
writable: true,
enumerable: true,
configurable: true,
})
);
global.$DefineOwnProperty("window", {
value: global,
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty(
"clearTimeout",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.clearTimeout", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.clearTimeout");
invariant(realm.generator !== undefined);
let generator = realm.generator;
generator.emitCall("global.clearTimeout", args);
return realm.intrinsics.undefined;
}),
writable: true,
enumerable: true,
configurable: true,
})
);
global.$DefineOwnProperty("document", {
value: initializeDocument(realm),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"setInterval",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.setInterval", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.setInterval");
let callback = args[0].throwIfNotConcrete();
if (!(callback instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "callback arguments must be function");
invariant(realm.generator !== undefined);
let generator = realm.generator;
return generator.emitCallAndCaptureResult(TypesDomain.topVal, ValuesDomain.topVal, "global.setInterval", args);
}),
writable: true,
enumerable: true,
configurable: true,
})
);
global.$DefineOwnProperty("setTimeout", {
value: new NativeFunctionValue(realm, "global.setTimeout", "", 2, (context, args) => {
let callback = args[0].throwIfNotConcrete();
if (!(callback instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "callback arguments must be function");
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.setTimeout");
invariant(realm.generator !== undefined);
let generator = realm.generator;
return generator.emitCallAndCaptureResult(TypesDomain.topVal, ValuesDomain.topVal, "global.setTimeout", args);
}),
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty("clearTimeout", {
value: new NativeFunctionValue(realm, "global.clearTimeout", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.clearTimeout");
invariant(realm.generator !== undefined);
let generator = realm.generator;
generator.emitCall("global.clearTimeout", args);
return realm.intrinsics.undefined;
}),
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty("setInterval", {
value: new NativeFunctionValue(realm, "global.setInterval", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.setInterval");
let callback = args[0].throwIfNotConcrete();
if (!(callback instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "callback arguments must be function");
invariant(realm.generator !== undefined);
let generator = realm.generator;
return generator.emitCallAndCaptureResult(TypesDomain.topVal, ValuesDomain.topVal, "global.setInterval", args);
}),
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty("clearInterval", {
value: new NativeFunctionValue(realm, "global.clearInterval", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.clearInterval");
invariant(realm.generator !== undefined);
let generator = realm.generator;
generator.emitCall("global.clearInterval", args);
return realm.intrinsics.undefined;
}),
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty(
"clearInterval",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.clearInterval", "", 2, (context, args) => {
if (!realm.useAbstractInterpretation) throw new FatalError("TODO #1003: implement global.clearInterval");
invariant(realm.generator !== undefined);
let generator = realm.generator;
generator.emitCall("global.clearInterval", args);
return realm.intrinsics.undefined;
}),
writable: true,
enumerable: true,
configurable: true,
})
);
}

View File

@ -1749,7 +1749,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
let elem = O.$GetOwnProperty(i.toString());
// b.If elem is undefined, return true.
if (elem === undefined) return true;
Properties.ThrowIfMightHaveBeenDeleted(elem.value);
Properties.ThrowIfMightHaveBeenDeleted(elem);
}
// 2.Return false.
return false;
@ -1782,8 +1782,8 @@ export default function(realm: Realm, obj: ObjectValue): void {
for (let j = 0; j < len; j++) {
// is a data property whose [[Configurable]] attribute is false.
let prop = O.$GetOwnProperty(j.toString());
if (prop !== undefined && !prop.configurable) {
Properties.ThrowIfMightHaveBeenDeleted(prop.value);
if (prop !== undefined && !prop.throwIfNotConcrete(realm).configurable) {
Properties.ThrowIfMightHaveBeenDeleted(prop);
throw Error(
"Implementation defined behavior : Array is sparse and it's prototype has some numbered properties"
);
@ -1795,8 +1795,8 @@ export default function(realm: Realm, obj: ObjectValue): void {
for (let j = 0; j < len; j++) {
//is a data property whose [[writable]] attribute is false.
let prop = O.$GetOwnProperty(j.toString());
if (prop !== undefined && !prop.writable) {
Properties.ThrowIfMightHaveBeenDeleted(prop.value);
if (prop !== undefined && !prop.throwIfNotConcrete(realm).writable) {
Properties.ThrowIfMightHaveBeenDeleted(prop);
throw Error("Implementation defined behavior : property " + j.toString() + "is non writable : ");
}
}

View File

@ -23,6 +23,7 @@ import { Get } from "../../methods/index.js";
import { Create, Properties, To } from "../../singletons.js";
import invariant from "../../invariant.js";
import type { BabelNodeSourceLocation } from "@babel/types";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): NativeFunctionValue {
return build("Error", realm, false);
@ -141,12 +142,12 @@ export function build(name: string, realm: Realm, inheritError?: boolean = true)
let msg = message.getType() === StringValue ? message : To.ToStringValue(realm, message);
// b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let msgDesc = {
let msgDesc = new PropertyDescriptor({
value: msg,
writable: true,
enumerable: false,
configurable: true,
};
});
// c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
Properties.DefinePropertyOrThrow(realm, O, "message", msgDesc);
@ -155,12 +156,12 @@ export function build(name: string, realm: Realm, inheritError?: boolean = true)
}
// Build a text description of the stack.
let stackDesc = {
let stackDesc = new PropertyDescriptor({
value: buildStack(realm, O),
enumerable: false,
configurable: true,
writable: true,
};
});
Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc);
// 4. Return O.

View File

@ -29,6 +29,7 @@ import { IsCallable } from "../../methods/is.js";
import { HasOwnProperty, HasSomeCompatibleType } from "../../methods/has.js";
import { OrdinaryHasInstance } from "../../methods/abstract.js";
import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 19.2.3
@ -129,12 +130,17 @@ export default function(realm: Realm, obj: ObjectValue): void {
}
// 8. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, F, "length", {
value: new NumberValue(realm, L),
writable: false,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, L),
writable: false,
enumerable: false,
configurable: true,
})
);
// 9. Let targetName be ? Get(Target, "name").
let targetName = Get(realm, Target, new StringValue(realm, "name"));

View File

@ -292,9 +292,10 @@ function InternalCloneObject(realm: Realm, val: ObjectValue): ObjectValue {
let clone = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
for (let [key, binding] of val.properties) {
if (binding === undefined || binding.descriptor === undefined) continue; // deleted
invariant(binding.descriptor !== undefined);
let value = binding.descriptor.value;
Properties.ThrowIfMightHaveBeenDeleted(value);
let desc = binding.descriptor;
invariant(desc !== undefined);
Properties.ThrowIfMightHaveBeenDeleted(desc);
let value = desc.throwIfNotConcrete(realm).value;
if (value === undefined) {
AbstractValue.reportIntrospectionError(val, key); // cannot handle accessors
throw new FatalError();

View File

@ -14,6 +14,7 @@ import { NumberValue, StringValue, NativeFunctionValue, ObjectValue } from "../.
import { Call, CreateMapIterator, IsCallable, SameValueZeroPartial } from "../../methods/index.js";
import { Properties } from "../../singletons.js";
import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.1.3.1
@ -257,39 +258,42 @@ export default function(realm: Realm, obj: ObjectValue): void {
});
// ECMA262 23.1.3.10
obj.$DefineOwnProperty("size", {
configurable: true,
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => {
// 1. Let M be the this value.
let M = context.throwIfNotConcrete();
obj.$DefineOwnProperty(
"size",
new PropertyDescriptor({
configurable: true,
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => {
// 1. Let M be the this value.
let M = context.throwIfNotConcrete();
// 2. If Type(M) is not Object, throw a TypeError exception.
if (!(M instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 2. If Type(M) is not Object, throw a TypeError exception.
if (!(M instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
if (!M.$MapData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
if (!M.$MapData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 4. Let entries be the List that is the value of M's [[MapData]] internal slot.
let entries = M.$MapData;
invariant(entries !== undefined);
// 4. Let entries be the List that is the value of M's [[MapData]] internal slot.
let entries = M.$MapData;
invariant(entries !== undefined);
// 5. Let count be 0.
let count = 0;
// 5. Let count be 0.
let count = 0;
// 6. For each Record {[[Key]], [[Value]]} p that is an element of entries
for (let p of entries) {
// a. If p.[[Key]] is not empty, set count to count+1.
if (p.$Key !== undefined) count++;
}
// 6. For each Record {[[Key]], [[Value]]} p that is an element of entries
for (let p of entries) {
// a. If p.[[Key]] is not empty, set count to count+1.
if (p.$Key !== undefined) count++;
}
// 7. Return count.
return new NumberValue(realm, count);
}),
});
// 7. Return count.
return new NumberValue(realm, count);
}),
})
);
// ECMA262 23.1.3.11
obj.defineNativeMethod("values", 0, context => {
@ -302,9 +306,9 @@ export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.1.3.12
let entriesPropertyDescriptor = obj.$GetOwnProperty("entries");
invariant(entriesPropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(entriesPropertyDescriptor.value);
obj.defineNativeProperty(realm.intrinsics.SymbolIterator, undefined, entriesPropertyDescriptor);
invariant(entriesPropertyDescriptor instanceof PropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(entriesPropertyDescriptor);
obj.$DefineOwnProperty(realm.intrinsics.SymbolIterator, entriesPropertyDescriptor);
// ECMA262 23.1.3.13
obj.defineNativeProperty(realm.intrinsics.SymbolToStringTag, new StringValue(realm, "Map"), { writable: false });

View File

@ -40,6 +40,7 @@ import {
import { Create, Leak, Properties as Props, To } from "../../singletons.js";
import { createOperationDescriptor } from "../../utils/generator.js";
import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
function snapshotToObjectAndRemoveProperties(
to: ObjectValue | AbstractObjectValue,
@ -96,8 +97,8 @@ function copyKeys(realm: Realm, keys, from, to): void {
let desc = from.$GetOwnProperty(nextKey);
// ii. If desc is not undefined and desc.[[Enumerable]] is true, then
if (desc && desc.enumerable) {
Props.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc && desc.throwIfNotConcrete(realm).enumerable) {
Props.ThrowIfMightHaveBeenDeleted(desc);
// 1. Let propValue be ? Get(from, nextKey).
let propValue = Get(realm, from, nextKey);
@ -346,35 +347,37 @@ export default function(realm: Realm): NativeFunctionValue {
// 3. Let desc be ? obj.[[GetOwnProperty]](key).
let desc = obj.$GetOwnProperty(key);
let getterFunc = desc && desc.get;
// If we are returning a descriptor with a NativeFunctionValue
// and it has no intrinsic name, then we create a temporal as this
// can only be done at runtime
if (
getterFunc instanceof NativeFunctionValue &&
getterFunc.intrinsicName === undefined &&
realm.useAbstractInterpretation
) {
invariant(P instanceof Value);
// this will create a property descriptor at runtime
let result = AbstractValue.createTemporalFromBuildFunction(
realm,
ObjectValue,
[getOwnPropertyDescriptor, obj, P],
createOperationDescriptor("OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR")
);
invariant(result instanceof AbstractObjectValue);
result.makeSimple();
let get = Get(realm, result, "get");
let set = Get(realm, result, "set");
invariant(get instanceof AbstractValue);
invariant(set instanceof AbstractValue);
desc = {
get,
set,
enumerable: false,
configurable: true,
};
if (desc instanceof PropertyDescriptor) {
let getterFunc = desc.get;
if (
getterFunc instanceof NativeFunctionValue &&
getterFunc.intrinsicName === undefined &&
realm.useAbstractInterpretation
) {
invariant(P instanceof Value);
// this will create a property descriptor at runtime
let result = AbstractValue.createTemporalFromBuildFunction(
realm,
ObjectValue,
[getOwnPropertyDescriptor, obj, P],
createOperationDescriptor("OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR")
);
invariant(result instanceof AbstractObjectValue);
result.makeSimple();
let get = Get(realm, result, "get");
let set = Get(realm, result, "set");
invariant(get instanceof AbstractValue);
invariant(set instanceof AbstractValue);
desc = new PropertyDescriptor({
get,
set,
enumerable: false,
configurable: true,
});
}
}
// 4. Return FromPropertyDescriptor(desc).
@ -404,7 +407,7 @@ export default function(realm: Realm): NativeFunctionValue {
for (let key of ownKeys) {
// a. Let desc be ? obj.[[GetOwnProperty]](key).
let desc = obj.$GetOwnProperty(key);
if (desc !== undefined) Props.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc !== undefined) Props.ThrowIfMightHaveBeenDeleted(desc);
// b. Let descriptor be ! FromPropertyDescriptor(desc).
let descriptor = Props.FromPropertyDescriptor(realm, desc);

View File

@ -26,6 +26,7 @@ import { FatalError } from "../../errors.js";
import invariant from "../../invariant.js";
import { TypesDomain, ValuesDomain } from "../../domains/index.js";
import { createOperationDescriptor } from "../../utils/generator.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 19.1.3.2
@ -100,7 +101,8 @@ export default function(realm: Realm, obj: ObjectValue): void {
// 4. If desc is undefined, return false.
if (!desc) return realm.intrinsics.false;
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
Properties.ThrowIfMightHaveBeenDeleted(desc);
desc = desc.throwIfNotConcrete(realm);
// 5. Return the value of desc.[[Enumerable]].
return desc.enumerable === undefined ? realm.intrinsics.undefined : new BooleanValue(realm, desc.enumerable);
@ -124,38 +126,41 @@ export default function(realm: Realm, obj: ObjectValue): void {
return To.ToObject(realm, context);
});
obj.$DefineOwnProperty("__proto__", {
// B.2.2.1.1
get: new NativeFunctionValue(realm, undefined, "get __proto__", 0, context => {
// 1. Let O be ? ToObject(this value).
let O = To.ToObject(realm, context);
obj.$DefineOwnProperty(
"__proto__",
new PropertyDescriptor({
// B.2.2.1.1
get: new NativeFunctionValue(realm, undefined, "get __proto__", 0, context => {
// 1. Let O be ? ToObject(this value).
let O = To.ToObject(realm, context);
// 2. Return ? O.[[GetPrototypeOf]]().
return O.$GetPrototypeOf();
}),
// 2. Return ? O.[[GetPrototypeOf]]().
return O.$GetPrototypeOf();
}),
// B.2.2.1.2
set: new NativeFunctionValue(realm, undefined, "set __proto__", 1, (context, [proto]) => {
// 1. Let O be ? RequireObjectCoercible(this value).
let O = RequireObjectCoercible(realm, context);
// B.2.2.1.2
set: new NativeFunctionValue(realm, undefined, "set __proto__", 1, (context, [proto]) => {
// 1. Let O be ? RequireObjectCoercible(this value).
let O = RequireObjectCoercible(realm, context);
// 2. If Type(proto) is neither Object nor Null, return undefined.
if (!HasSomeCompatibleType(proto, ObjectValue, NullValue)) return realm.intrinsics.undefined;
// 2. If Type(proto) is neither Object nor Null, return undefined.
if (!HasSomeCompatibleType(proto, ObjectValue, NullValue)) return realm.intrinsics.undefined;
// 3. If Type(O) is not Object, return undefined.
if (!O.mightBeObject()) return realm.intrinsics.undefined;
O = O.throwIfNotConcreteObject();
// 3. If Type(O) is not Object, return undefined.
if (!O.mightBeObject()) return realm.intrinsics.undefined;
O = O.throwIfNotConcreteObject();
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
let status = O.$SetPrototypeOf(((proto.throwIfNotConcrete(): any): ObjectValue | NullValue));
// 4. Let status be ? O.[[SetPrototypeOf]](proto).
let status = O.$SetPrototypeOf(((proto.throwIfNotConcrete(): any): ObjectValue | NullValue));
// 5. If status is false, throw a TypeError exception.
if (!status) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "couldn't set proto");
}
// 5. If status is false, throw a TypeError exception.
if (!status) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "couldn't set proto");
}
// 6. Return undefined.
return realm.intrinsics.undefined;
}),
});
// 6. Return undefined.
return realm.intrinsics.undefined;
}),
})
);
}

View File

@ -14,6 +14,7 @@ import { NativeFunctionValue, ObjectValue, StringValue, NumberValue } from "../.
import { Call, CreateSetIterator, IsCallable, SameValueZeroPartial } from "../../methods/index.js";
import { Properties } from "../../singletons.js";
import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.2.3.1
@ -200,38 +201,41 @@ export default function(realm: Realm, obj: ObjectValue): void {
});
// ECMA262 23.2.3.9 get Set.prototype.size
obj.$DefineOwnProperty("size", {
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => {
// 1. Let S be the this value.
let S = context.throwIfNotConcrete();
obj.$DefineOwnProperty(
"size",
new PropertyDescriptor({
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => {
// 1. Let S be the this value.
let S = context.throwIfNotConcrete();
// 2. If Type(S) is not Object, throw a TypeError exception.
if (!(S instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 2. If Type(S) is not Object, throw a TypeError exception.
if (!(S instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
if (!S.$SetData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
if (!S.$SetData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
// 4. Let entries be the List that is the value of S's [[SetData]] internal slot.
let entries = S.$SetData;
// 4. Let entries be the List that is the value of S's [[SetData]] internal slot.
let entries = S.$SetData;
// 5. Let count be 0.
let count = 0;
// 5. Let count be 0.
let count = 0;
// 6. For each e that is an element of entries
for (let e of entries) {
// a. If e is not empty, set count to count+1.
if (e) count++;
}
// 6. For each e that is an element of entries
for (let e of entries) {
// a. If e is not empty, set count to count+1.
if (e) count++;
}
// 7. Return count.
return new NumberValue(realm, count);
}),
configurable: true,
});
// 7. Return count.
return new NumberValue(realm, count);
}),
configurable: true,
})
);
// ECMA262 23.2.3.10
obj.defineNativeMethod("values", 0, context => {
@ -244,12 +248,12 @@ export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.2.3.8
let valuesPropertyDescriptor = obj.$GetOwnProperty("values");
invariant(valuesPropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(valuesPropertyDescriptor.value);
obj.defineNativeProperty("keys", undefined, valuesPropertyDescriptor);
invariant(valuesPropertyDescriptor instanceof PropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(valuesPropertyDescriptor);
obj.$DefineOwnProperty("keys", valuesPropertyDescriptor);
// ECMA262 23.2.3.11
obj.defineNativeProperty(realm.intrinsics.SymbolIterator, undefined, valuesPropertyDescriptor);
obj.$DefineOwnProperty(realm.intrinsics.SymbolIterator, valuesPropertyDescriptor);
// ECMA262 23.2.3.12 Set.prototype [ @@toStringTag ]
obj.defineNativeProperty(realm.intrinsics.SymbolToStringTag, new StringValue(realm, "Set"), { writable: false });

View File

@ -11,6 +11,7 @@
import type { Realm } from "../../realm.js";
import { NativeFunctionValue } from "../../values/index.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): NativeFunctionValue {
// ECMA262 9.2.7.1
@ -22,12 +23,15 @@ export default function(realm: Realm): NativeFunctionValue {
func.setExtensible(false);
// ECMA262 9.2.7.1
func.$DefineOwnProperty("length", {
value: realm.intrinsics.zero,
writable: false,
configurable: false,
enumerable: false,
});
func.$DefineOwnProperty(
"length",
new PropertyDescriptor({
value: realm.intrinsics.zero,
writable: false,
configurable: false,
enumerable: false,
})
);
return func;
}

View File

@ -11,24 +11,31 @@
import type { Realm } from "../../realm.js";
import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
global.$DefineOwnProperty("global", {
value: global,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"global",
new PropertyDescriptor({
value: global,
writable: true,
enumerable: false,
configurable: true,
})
);
for (let name of ["undefined", "NaN", "Infinity"]) {
global.$DefineOwnProperty(name, {
value: realm.intrinsics[name],
writable: false,
enumerable: false,
configurable: false,
});
global.$DefineOwnProperty(
name,
new PropertyDescriptor({
value: realm.intrinsics[name],
writable: false,
enumerable: false,
configurable: false,
})
);
}
let typeNames = [
"String",
@ -69,12 +76,15 @@ export default function(realm: Realm): void {
for (let name of typeNames) {
// need to check if the property exists (it may not due to --compatibility)
if (realm.intrinsics[name]) {
global.$DefineOwnProperty(name, {
value: realm.intrinsics[name],
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
name,
new PropertyDescriptor({
value: realm.intrinsics[name],
writable: true,
enumerable: false,
configurable: true,
})
);
} else {
invariant(
name === "Symbol" || name === "Promise" || name === "WeakSet" || name === "Proxy" || name === "Reflect"
@ -94,11 +104,14 @@ export default function(realm: Realm): void {
"encodeURIComponent",
"decodeURIComponent",
]) {
global.$DefineOwnProperty(name, {
value: realm.intrinsics[name],
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
name,
new PropertyDescriptor({
value: realm.intrinsics[name],
writable: true,
enumerable: false,
configurable: true,
})
);
}
}

View File

@ -27,6 +27,7 @@ import invariant from "../../invariant.js";
import { Properties } from "../../singletons.js";
import { forEachArrayValue } from "../../react/utils.js";
import { createOperationDescriptor } from "../../utils/generator.js";
import { PropertyDescriptor } from "../../descriptors.js";
const fbMagicGlobalFunctions = [
"asset",
@ -74,8 +75,7 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
return superClass;
});
babelHelpersValue.$DefineOwnProperty("inherits", {
value: inheritsValue,
babelHelpersValue.defineNativeProperty("inherits", inheritsValue, {
writable: false,
enumerable: false,
configurable: true,
@ -92,7 +92,7 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
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) {
if (binding && binding.descriptor && binding.descriptor.throwIfNotConcrete(realm).enumerable) {
let value = Get(realm, obj, propName);
Properties.Set(realm, newObject, propName, value, true);
}
@ -134,8 +134,7 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
}
}
);
babelHelpersValue.$DefineOwnProperty("objectWithoutProperties", {
value: objectWithoutPropertiesValue,
babelHelpersValue.defineNativeProperty("objectWithoutProperties", objectWithoutPropertiesValue, {
writable: false,
enumerable: false,
configurable: true,
@ -154,8 +153,7 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
return strings;
}
);
babelHelpersValue.$DefineOwnProperty("taggedTemplateLiteralLoose", {
value: taggedTemplateLiteralLooseValue,
babelHelpersValue.defineNativeProperty("taggedTemplateLiteralLoose", taggedTemplateLiteralLooseValue, {
writable: false,
enumerable: false,
configurable: true,
@ -163,15 +161,13 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
taggedTemplateLiteralLooseValue.intrinsicName = `babelHelpers.taggedTemplateLiteralLoose`;
//babelHelpers.extends & babelHelpers._extends
babelHelpersValue.$DefineOwnProperty("extends", {
value: objectAssign,
babelHelpersValue.defineNativeProperty("extends", objectAssign, {
writable: true,
enumerable: true,
configurable: true,
});
babelHelpersValue.$DefineOwnProperty("_extends", {
value: objectAssign,
babelHelpersValue.defineNativeProperty("_extends", objectAssign, {
writable: true,
enumerable: true,
configurable: true,
@ -180,51 +176,59 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
//babelHelpers.bind
let functionBind = Get(realm, realm.intrinsics.FunctionPrototype, "bind");
babelHelpersValue.$DefineOwnProperty("bind", {
value: functionBind,
babelHelpersValue.defineNativeProperty("bind", functionBind, {
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty("babelHelpers", {
value: babelHelpersValue,
writable: true,
enumerable: true,
configurable: true,
});
global.$DefineOwnProperty(
"babelHelpers",
new PropertyDescriptor({
value: babelHelpersValue,
writable: true,
enumerable: true,
configurable: true,
})
);
babelHelpersValue.refuseSerialization = false;
}
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,
[new StringValue(realm, functionName), ...args],
createOperationDescriptor("FB_MOCKS_MAGIC_GLOBAL_FUNCTION"),
{ skipInvariant: true, isPure: true }
);
invariant(val instanceof AbstractValue);
return val;
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
functionName,
new PropertyDescriptor({
value: new NativeFunctionValue(realm, functionName, functionName, 0, (context, args) => {
let val = AbstractValue.createTemporalFromBuildFunction(
realm,
FunctionValue,
[new StringValue(realm, functionName), ...args],
createOperationDescriptor("FB_MOCKS_MAGIC_GLOBAL_FUNCTION"),
{ skipInvariant: true, isPure: true }
);
invariant(val instanceof AbstractValue);
return val;
}),
writable: true,
enumerable: false,
configurable: true,
})
);
}
function createMagicGlobalObject(realm: Realm, global: ObjectValue | AbstractObjectValue, objectName: string) {
let globalObject = AbstractValue.createAbstractObject(realm, objectName);
globalObject.kind = "resolved";
global.$DefineOwnProperty(objectName, {
value: globalObject,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
objectName,
new PropertyDescriptor({
value: globalObject,
writable: true,
enumerable: false,
configurable: true,
})
);
}
function createBootloader(realm: Realm, global: ObjectValue | AbstractObjectValue) {
@ -245,24 +249,30 @@ function createBootloader(realm: Realm, global: ObjectValue | AbstractObjectValu
Properties.Set(realm, bootloader, "loadModules", loadModules, false);
global.$DefineOwnProperty("Bootloader", {
value: bootloader,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"Bootloader",
new PropertyDescriptor({
value: bootloader,
writable: true,
enumerable: false,
configurable: true,
})
);
return AbstractValue.createAbstractObject(realm, "Bootloader", bootloader);
}
export function createFbMocks(realm: Realm, global: ObjectValue | AbstractObjectValue): void {
global.$DefineOwnProperty("__DEV__", {
// TODO: we'll likely want to make this configurable from the outside.
value: realm.intrinsics.false,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__DEV__",
new PropertyDescriptor({
// TODO: we'll likely want to make this configurable from the outside.
value: realm.intrinsics.false,
writable: true,
enumerable: false,
configurable: true,
})
);
createBabelHelpers(realm, global);

View File

@ -21,6 +21,7 @@ import { FatalError } from "../../errors";
import { Get } from "../../methods/index.js";
import invariant from "../../invariant";
import { createDefaultPropsHelper } from "../../react/utils.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
@ -42,84 +43,93 @@ export default function(realm: Realm): void {
let moduleExportsValue = AbstractValue.createAbstractObject(realm, "module.exports");
moduleExportsValue.kind = "resolved";
moduleValue.$DefineOwnProperty("exports", {
value: moduleExportsValue,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty("module", {
value: moduleValue,
writable: true,
enumerable: false,
configurable: true,
});
moduleValue.$DefineOwnProperty(
"exports",
new PropertyDescriptor({
value: moduleExportsValue,
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty(
"module",
new PropertyDescriptor({
value: moduleValue,
writable: true,
enumerable: false,
configurable: true,
})
);
// apply require() mock
global.$DefineOwnProperty("require", {
value: new NativeFunctionValue(realm, "require", "require", 0, (context, [requireNameVal]) => {
invariant(requireNameVal instanceof StringValue);
let requireNameValValue = requireNameVal.value;
global.$DefineOwnProperty(
"require",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "require", "require", 0, (context, [requireNameVal]) => {
invariant(requireNameVal instanceof StringValue);
let requireNameValValue = requireNameVal.value;
if (requireNameValValue === "react" || requireNameValValue === "React") {
if (realm.fbLibraries.react === undefined) {
let react = createMockReact(realm, requireNameValValue);
realm.fbLibraries.react = react;
return react;
}
return realm.fbLibraries.react;
} else if (requireNameValValue === "react-dom" || requireNameValValue === "ReactDOM") {
if (realm.fbLibraries.reactDom === undefined) {
let reactDom = createMockReactDOM(realm, requireNameValValue);
realm.fbLibraries.reactDom = reactDom;
return reactDom;
}
return realm.fbLibraries.reactDom;
} else if (requireNameValValue === "react-dom/server" || requireNameValValue === "ReactDOMServer") {
if (realm.fbLibraries.reactDomServer === undefined) {
let reactDomServer = createMockReactDOMServer(realm, requireNameValValue);
realm.fbLibraries.reactDomServer = reactDomServer;
return reactDomServer;
}
return realm.fbLibraries.reactDomServer;
} else if (requireNameValValue === "react-native" || requireNameValValue === "ReactNative") {
if (realm.fbLibraries.reactNative === undefined) {
let reactNative = createMockReactNative(realm, requireNameValValue);
realm.fbLibraries.reactNative = reactNative;
return reactNative;
}
return realm.fbLibraries.reactNative;
} else if (requireNameValValue === "react-relay" || requireNameValValue === "RelayModern") {
if (realm.fbLibraries.reactRelay === undefined) {
let reactRelay = createMockReactRelay(realm, requireNameValValue);
realm.fbLibraries.reactRelay = reactRelay;
return reactRelay;
}
return realm.fbLibraries.reactRelay;
} else if (requireNameValValue === "prop-types" || requireNameValValue === "PropTypes") {
if (realm.fbLibraries.react === undefined) {
throw new FatalError("unable to require PropTypes due to React not being referenced in scope");
}
let propTypes = Get(realm, realm.fbLibraries.react, "PropTypes");
invariant(propTypes instanceof ObjectValue);
return propTypes;
} else {
let requireVal;
if (realm.fbLibraries.other.has(requireNameValValue)) {
requireVal = realm.fbLibraries.other.get(requireNameValValue);
if (requireNameValValue === "react" || requireNameValValue === "React") {
if (realm.fbLibraries.react === undefined) {
let react = createMockReact(realm, requireNameValValue);
realm.fbLibraries.react = react;
return react;
}
return realm.fbLibraries.react;
} else if (requireNameValValue === "react-dom" || requireNameValValue === "ReactDOM") {
if (realm.fbLibraries.reactDom === undefined) {
let reactDom = createMockReactDOM(realm, requireNameValValue);
realm.fbLibraries.reactDom = reactDom;
return reactDom;
}
return realm.fbLibraries.reactDom;
} else if (requireNameValValue === "react-dom/server" || requireNameValValue === "ReactDOMServer") {
if (realm.fbLibraries.reactDomServer === undefined) {
let reactDomServer = createMockReactDOMServer(realm, requireNameValValue);
realm.fbLibraries.reactDomServer = reactDomServer;
return reactDomServer;
}
return realm.fbLibraries.reactDomServer;
} else if (requireNameValValue === "react-native" || requireNameValValue === "ReactNative") {
if (realm.fbLibraries.reactNative === undefined) {
let reactNative = createMockReactNative(realm, requireNameValValue);
realm.fbLibraries.reactNative = reactNative;
return reactNative;
}
return realm.fbLibraries.reactNative;
} else if (requireNameValValue === "react-relay" || requireNameValValue === "RelayModern") {
if (realm.fbLibraries.reactRelay === undefined) {
let reactRelay = createMockReactRelay(realm, requireNameValValue);
realm.fbLibraries.reactRelay = reactRelay;
return reactRelay;
}
return realm.fbLibraries.reactRelay;
} else if (requireNameValValue === "prop-types" || requireNameValValue === "PropTypes") {
if (realm.fbLibraries.react === undefined) {
throw new FatalError("unable to require PropTypes due to React not being referenced in scope");
}
let propTypes = Get(realm, realm.fbLibraries.react, "PropTypes");
invariant(propTypes instanceof ObjectValue);
return propTypes;
} else {
requireVal = createAbstract(realm, "function", `require("${requireNameValValue}")`);
realm.fbLibraries.other.set(requireNameValValue, requireVal);
let requireVal;
if (realm.fbLibraries.other.has(requireNameValValue)) {
requireVal = realm.fbLibraries.other.get(requireNameValValue);
} else {
requireVal = createAbstract(realm, "function", `require("${requireNameValValue}")`);
realm.fbLibraries.other.set(requireNameValValue, requireVal);
}
invariant(requireVal instanceof AbstractValue);
return requireVal;
}
invariant(requireVal instanceof AbstractValue);
return requireVal;
}
}),
writable: true,
enumerable: false,
configurable: true,
});
}),
writable: true,
enumerable: false,
configurable: true,
})
);
if (realm.isCompatibleWith("fb-www")) {
createFbMocks(realm, global);

View File

@ -52,8 +52,7 @@ export function addMockFunctionToObject(
): void {
let funcValue = new NativeFunctionValue(realm, undefined, funcName, 0, (context, args) => func(funcValue, args));
obj.$DefineOwnProperty(funcName, {
value: funcValue,
obj.defineNativeProperty(funcName, funcValue, {
writable: false,
enumerable: false,
configurable: true,

View File

@ -158,6 +158,7 @@ import initializeThrowTypeError from "./ecma262/ThrowTypeError.js";
import initialize__IntrospectionError from "./prepack/__IntrospectionError.js";
import initialize__IntrospectionErrorPrototype from "./prepack/__IntrospectionErrorPrototype.js";
import { PropertyDescriptor } from "../descriptors.js";
export function initialize(i: Intrinsics, realm: Realm): Intrinsics {
i.undefined = new UndefinedValue(realm);
@ -401,26 +402,35 @@ export function initialize(i: Intrinsics, realm: Realm): Intrinsics {
let fn = i[name];
let proto = i[`${name}Prototype`];
proto.$DefineOwnProperty("constructor", {
value: fn,
writable: true,
enumerable: false,
configurable: true,
});
proto.$DefineOwnProperty(
"constructor",
new PropertyDescriptor({
value: fn,
writable: true,
enumerable: false,
configurable: true,
})
);
fn.$DefineOwnProperty("prototype", {
value: proto,
writable: false,
enumerable: false,
configurable: false,
});
fn.$DefineOwnProperty(
"prototype",
new PropertyDescriptor({
value: proto,
writable: false,
enumerable: false,
configurable: false,
})
);
fn.$DefineOwnProperty("constructor", {
value: i.Function,
writable: true,
enumerable: false,
configurable: true,
});
fn.$DefineOwnProperty(
"constructor",
new PropertyDescriptor({
value: i.Function,
writable: true,
enumerable: false,
configurable: true,
})
);
}
//
@ -430,31 +440,43 @@ export function initialize(i: Intrinsics, realm: Realm): Intrinsics {
initializeGenerator(realm, i.Generator);
i.GeneratorFunction = initializeGeneratorFunction(realm);
i.Generator.$DefineOwnProperty("prototype", {
value: i.GeneratorPrototype,
writable: false,
enumerable: false,
configurable: true,
});
i.GeneratorPrototype.$DefineOwnProperty("constructor", {
value: i.Generator,
writable: false,
enumerable: false,
configurable: true,
});
i.Generator.$DefineOwnProperty(
"prototype",
new PropertyDescriptor({
value: i.GeneratorPrototype,
writable: false,
enumerable: false,
configurable: true,
})
);
i.GeneratorPrototype.$DefineOwnProperty(
"constructor",
new PropertyDescriptor({
value: i.Generator,
writable: false,
enumerable: false,
configurable: true,
})
);
i.GeneratorFunction.$DefineOwnProperty("prototype", {
value: i.Generator,
writable: false,
enumerable: false,
configurable: false,
});
i.Generator.$DefineOwnProperty("constructor", {
value: i.GeneratorFunction,
writable: false,
enumerable: false,
configurable: true,
});
i.GeneratorFunction.$DefineOwnProperty(
"prototype",
new PropertyDescriptor({
value: i.Generator,
writable: false,
enumerable: false,
configurable: false,
})
);
i.Generator.$DefineOwnProperty(
"constructor",
new PropertyDescriptor({
value: i.GeneratorFunction,
writable: false,
enumerable: false,
configurable: true,
})
);
//
i.isNaN = initializeIsNaN(realm);

View File

@ -34,6 +34,7 @@ import { CompilerDiagnostic, FatalError } from "../../errors.js";
import * as t from "@babel/types";
import { createOperationDescriptor, type OperationDescriptor } from "../../utils/generator.js";
import { createAndValidateArgModel } from "../../utils/ShapeInformation";
import { PropertyDescriptor } from "../../descriptors.js";
export function createAbstractFunction(realm: Realm, ...additionalValues: Array<ConcreteValue>): NativeFunctionValue {
return new NativeFunctionValue(
@ -61,15 +62,18 @@ export function createAbstractFunction(realm: Realm, ...additionalValues: Array<
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
global.$DefineOwnProperty("__dump", {
value: new NativeFunctionValue(realm, "global.__dump", "__dump", 0, (context, args) => {
console.log("dump", args.map(arg => arg.serialize()));
return context;
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__dump",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__dump", "__dump", 0, (context, args) => {
console.log("dump", args.map(arg => arg.serialize()));
return context;
}),
writable: true,
enumerable: false,
configurable: true,
})
);
// Helper function to model values that are obtained from the environment,
// and whose concrete values are not known at Prepack-time.
@ -86,33 +90,45 @@ export default function(realm: Realm): void {
// the abstract value's name.
// If the abstract value gets somehow embedded in the final heap,
// it will be referred to by the supplied name in the generated code.
global.$DefineOwnProperty("__abstract", {
value: createAbstractFunction(realm),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__abstract",
new PropertyDescriptor({
value: createAbstractFunction(realm),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrNull", {
value: createAbstractFunction(realm, realm.intrinsics.null),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__abstractOrNull",
new PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.null),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrNullOrUndefined", {
value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__abstractOrNullOrUndefined",
new PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrUndefined", {
value: createAbstractFunction(realm, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__abstractOrUndefined",
new PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true,
})
);
// Allows dynamically registering optimized functions.
// WARNING: these functions will get exposed at global scope and called there.
@ -120,156 +136,180 @@ export default function(realm: Realm): void {
// that is not subsequently applied, the function will not be registered
// (because prepack won't have a correct value for the FunctionValue itself)
// If we encounter an invalid input, we will emit a warning and not optimize the function
global.$DefineOwnProperty("__optimize", {
value: new NativeFunctionValue(realm, "global.__optimize", "__optimize", 1, (context, [value, argModelString]) => {
let argModel;
if (argModelString !== undefined) {
argModel = createAndValidateArgModel(realm, argModelString);
}
if (value instanceof ECMAScriptSourceFunctionValue || value instanceof AbstractValue) {
let currentArgModel = realm.optimizedFunctions.get(value);
// Verify that if there is an existing argModel, that it is the same as the new one.
if (currentArgModel) {
let currentString = argModelString instanceof StringValue ? argModelString.value : argModelString;
if (JSON.stringify(currentArgModel) !== currentString) {
let argModelError = new CompilerDiagnostic(
"__optimize called twice with different argModelStrings",
realm.currentLocation,
"PP1008",
"Warning"
global.$DefineOwnProperty(
"__optimize",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__optimize",
"__optimize",
1,
(context, [value, argModelString]) => {
let argModel;
if (argModelString !== undefined) {
argModel = createAndValidateArgModel(realm, argModelString);
}
if (value instanceof ECMAScriptSourceFunctionValue || value instanceof AbstractValue) {
let currentArgModel = realm.optimizedFunctions.get(value);
// Verify that if there is an existing argModel, that it is the same as the new one.
if (currentArgModel) {
let currentString = argModelString instanceof StringValue ? argModelString.value : argModelString;
if (JSON.stringify(currentArgModel) !== currentString) {
let argModelError = new CompilerDiagnostic(
"__optimize called twice with different argModelStrings",
realm.currentLocation,
"PP1008",
"Warning"
);
if (realm.handleError(argModelError) !== "Recover") throw new FatalError();
else return realm.intrinsics.undefined;
}
}
realm.optimizedFunctions.set(value, argModel);
} else {
let location = value.expressionLocation
? `${value.expressionLocation.start.line}:${value.expressionLocation.start.column} ` +
`${value.expressionLocation.end.line}:${value.expressionLocation.end.line}`
: "location unknown";
let result = realm.handleError(
new CompilerDiagnostic(
`Optimized Function Value ${location} is an not a function or react element`,
realm.currentLocation,
"PP0033",
"Warning"
)
);
if (realm.handleError(argModelError) !== "Recover") throw new FatalError();
if (result !== "Recover") throw new FatalError();
else return realm.intrinsics.undefined;
}
return value;
}
realm.optimizedFunctions.set(value, argModel);
} else {
let location = value.expressionLocation
? `${value.expressionLocation.start.line}:${value.expressionLocation.start.column} ` +
`${value.expressionLocation.end.line}:${value.expressionLocation.end.line}`
: "location unknown";
let result = realm.handleError(
new CompilerDiagnostic(
`Optimized Function Value ${location} is an not a function or react element`,
realm.currentLocation,
"PP0033",
"Warning"
)
);
if (result !== "Recover") throw new FatalError();
else return realm.intrinsics.undefined;
}
return value;
}),
writable: true,
enumerable: false,
configurable: true,
});
),
writable: true,
enumerable: false,
configurable: true,
})
);
if (realm.react.enabled) {
global.$DefineOwnProperty("__reactComponentTrees", {
global.$DefineOwnProperty(
"__reactComponentTrees",
new PropertyDescriptor({
value: new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"__reactComponentTrees",
/* refuseSerialization */ true
),
writable: true,
enumerable: false,
configurable: true,
})
);
let reactComponentRootUid = 0;
global.$DefineOwnProperty(
"__optimizeReactComponentTree",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__optimizeReactComponentTree",
"__optimizeReactComponentTree",
0,
(context, [component, config]) => {
let hasValidComponent =
component instanceof ECMAScriptSourceFunctionValue || valueIsKnownReactAbstraction(realm, component);
let hasValidConfig =
config instanceof ObjectValue || config === realm.intrinsics.undefined || config === undefined;
if (!hasValidComponent || !hasValidConfig) {
let diagnostic = new CompilerDiagnostic(
"__optimizeReactComponentTree(rootComponent, config) has been called with invalid arguments",
realm.currentLocation,
"PP0024",
"FatalError"
);
realm.handleError(diagnostic);
if (realm.handleError(diagnostic) === "Fail") throw new FatalError();
}
let reactComponentTree = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
reactComponentTree.$Set("rootComponent", component, reactComponentTree);
reactComponentTree.$Set("config", config || realm.intrinsics.undefined, reactComponentTree);
realm.assignToGlobal(
t.memberExpression(
t.memberExpression(t.identifier("global"), t.identifier("__reactComponentTrees")),
t.identifier("" + reactComponentRootUid++)
),
reactComponentTree
);
return component;
}
),
writable: true,
enumerable: false,
configurable: true,
})
);
}
global.$DefineOwnProperty(
"__evaluatePureFunction",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__evaluatePureFunction",
"__evaluatePureFunction",
0,
(context, [functionValue]) => {
invariant(functionValue instanceof ECMAScriptSourceFunctionValue);
invariant(typeof functionValue.$Call === "function");
let functionCall: Function = functionValue.$Call;
return realm.evaluatePure(
() => functionCall(realm.intrinsics.undefined, []),
/*bubbles*/ true,
/*reportSideEffectFunc*/ null
);
}
),
writable: true,
enumerable: false,
configurable: true,
})
);
// Maps from initialized moduleId to exports object
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty(
"__initializedModules",
new PropertyDescriptor({
value: new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"__reactComponentTrees",
"__initializedModules",
/* refuseSerialization */ true
),
writable: true,
enumerable: false,
configurable: true,
});
let reactComponentRootUid = 0;
global.$DefineOwnProperty("__optimizeReactComponentTree", {
value: new NativeFunctionValue(
})
);
// Set of property bindings whose invariant got checked
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty(
"__checkedBindings",
new PropertyDescriptor({
value: new ObjectValue(
realm,
"global.__optimizeReactComponentTree",
"__optimizeReactComponentTree",
0,
(context, [component, config]) => {
let hasValidComponent =
component instanceof ECMAScriptSourceFunctionValue || valueIsKnownReactAbstraction(realm, component);
let hasValidConfig =
config instanceof ObjectValue || config === realm.intrinsics.undefined || config === undefined;
if (!hasValidComponent || !hasValidConfig) {
let diagnostic = new CompilerDiagnostic(
"__optimizeReactComponentTree(rootComponent, config) has been called with invalid arguments",
realm.currentLocation,
"PP0024",
"FatalError"
);
realm.handleError(diagnostic);
if (realm.handleError(diagnostic) === "Fail") throw new FatalError();
}
let reactComponentTree = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
reactComponentTree.$Set("rootComponent", component, reactComponentTree);
reactComponentTree.$Set("config", config || realm.intrinsics.undefined, reactComponentTree);
realm.assignToGlobal(
t.memberExpression(
t.memberExpression(t.identifier("global"), t.identifier("__reactComponentTrees")),
t.identifier("" + reactComponentRootUid++)
),
reactComponentTree
);
return component;
}
realm.intrinsics.ObjectPrototype,
"__checkedBindings",
/* refuseSerialization */ true
),
writable: true,
enumerable: false,
configurable: true,
});
}
global.$DefineOwnProperty("__evaluatePureFunction", {
value: new NativeFunctionValue(
realm,
"global.__evaluatePureFunction",
"__evaluatePureFunction",
0,
(context, [functionValue]) => {
invariant(functionValue instanceof ECMAScriptSourceFunctionValue);
invariant(typeof functionValue.$Call === "function");
let functionCall: Function = functionValue.$Call;
return realm.evaluatePure(
() => functionCall(realm.intrinsics.undefined, []),
/*bubbles*/ true,
/*reportSideEffectFunc*/ null
);
}
),
writable: true,
enumerable: false,
configurable: true,
});
// Maps from initialized moduleId to exports object
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty("__initializedModules", {
value: new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"__initializedModules",
/* refuseSerialization */ true
),
writable: true,
enumerable: false,
configurable: true,
});
// Set of property bindings whose invariant got checked
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty("__checkedBindings", {
value: new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"__checkedBindings",
/* refuseSerialization */ true
),
writable: true,
enumerable: false,
configurable: true,
});
})
);
// Helper function used to instantiate a residual function
function createNativeFunctionForResidualCall(unsafe: boolean): NativeFunctionValue {
@ -328,29 +368,32 @@ export default function(realm: Realm): void {
// Helper function that specifies a dynamic invariant that cannot be evaluated at prepack time, and needs code to
// be injected into the serialized output.
global.$DefineOwnProperty("__assume", {
value: createNativeFunctionForResidualInjection(
"__assume",
([c, s]): void => {
if (!c.mightBeTrue()) {
let error = new CompilerDiagnostic(
`Assumed condition cannot hold`,
realm.currentLocation,
"PP0040",
"FatalError"
);
realm.handleError(error);
throw new FatalError();
}
Path.pushAndRefine(c);
},
createOperationDescriptor("ASSUME_CALL"),
2
),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__assume",
new PropertyDescriptor({
value: createNativeFunctionForResidualInjection(
"__assume",
([c, s]): void => {
if (!c.mightBeTrue()) {
let error = new CompilerDiagnostic(
`Assumed condition cannot hold`,
realm.currentLocation,
"PP0040",
"FatalError"
);
realm.handleError(error);
throw new FatalError();
}
Path.pushAndRefine(c);
},
createOperationDescriptor("ASSUME_CALL"),
2
),
writable: true,
enumerable: false,
configurable: true,
})
);
// Helper function that identifies a computation that must remain part of the residual program and cannot be partially evaluated,
// e.g. because it contains a loop over abstract values.
@ -358,190 +401,226 @@ export default function(realm: Realm): void {
// that is computed by invoking function(arg0, arg1, ...) in the residual program and
// where typeNameOrTemplate either either 'string', 'boolean', 'number', 'object', or an actual object defining known properties.
// The function must not have side effects, and it must not access any state (besides the supplied arguments).
global.$DefineOwnProperty("__residual", {
value: createNativeFunctionForResidualCall(false),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__residual",
new PropertyDescriptor({
value: createNativeFunctionForResidualCall(false),
writable: true,
enumerable: false,
configurable: true,
})
);
// Helper function that identifies a variant of the residual function that has implicit dependencies. This version of residual will infer the dependencies
// and rewrite the function body to do the same thing as the original residual function.
global.$DefineOwnProperty("__residual_unsafe", {
value: createNativeFunctionForResidualCall(true),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__residual_unsafe",
new PropertyDescriptor({
value: createNativeFunctionForResidualCall(true),
writable: true,
enumerable: false,
configurable: true,
})
);
// Internal helper function for tests.
// __isAbstract(value) checks if a given value is abstract.
global.$DefineOwnProperty("__isAbstract", {
value: new NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => {
return new BooleanValue(realm, value instanceof AbstractValue);
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__isAbstract",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => {
return new BooleanValue(realm, value instanceof AbstractValue);
}),
writable: true,
enumerable: false,
configurable: true,
})
);
// __makePartial(object) marks an (abstract) object as partial.
global.$DefineOwnProperty("__makePartial", {
value: new NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => {
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
object.makePartial();
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__makePartial",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => {
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
object.makePartial();
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__makeFinal", {
value: new NativeFunctionValue(realm, "global.__makeFinal", "__makeFinal", 1, (context, [object]) => {
if (object instanceof ObjectValue || (object instanceof AbstractObjectValue && !object.values.isTop())) {
object.makeFinal();
return object;
}
throw realm.createErrorThrowCompletion(
realm.intrinsics.TypeError,
"not an object or abstract object value (non-top)"
);
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__makeFinal",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__makeFinal", "__makeFinal", 1, (context, [object]) => {
if (object instanceof ObjectValue || (object instanceof AbstractObjectValue && !object.values.isTop())) {
object.makeFinal();
return object;
}
throw realm.createErrorThrowCompletion(
realm.intrinsics.TypeError,
"not an object or abstract object value (non-top)"
);
}),
writable: true,
enumerable: false,
configurable: true,
})
);
// __makeSimple(object) marks an (abstract) object as one that has no getters or setters.
global.$DefineOwnProperty("__makeSimple", {
value: new NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => {
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
object.makeSimple(option);
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__makeSimple",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => {
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
object.makeSimple(option);
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true,
})
);
// Helper function that emits a check whether a given object property has a particular value.
global.$DefineOwnProperty("__assumeDataProperty", {
value: new NativeFunctionValue(
realm,
"global.__assumeDataProperty",
"__assumeDataProperty",
3,
(context, [object, propertyName, value, invariantOptions]) => {
if (!realm.useAbstractInterpretation) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
}
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
let generator = realm.generator;
invariant(generator);
let key = To.ToStringPartial(realm, propertyName);
if (realm.emitConcreteModel) {
generator.emitConcreteModel(key, value);
} else if (realm.invariantLevel >= 1) {
let invariantOptionString = invariantOptions
? To.ToStringPartial(realm, invariantOptions)
: "FULL_INVARIANT";
switch (invariantOptionString) {
// checks (!property in object || object.property === undefined)
case "VALUE_DEFINED_INVARIANT":
generator.emitPropertyInvariant(object, key, value.mightBeUndefined() ? "PRESENT" : "DEFINED");
break;
case "SKIP_INVARIANT":
break;
case "FULL_INVARIANT":
generator.emitFullInvariant((object: any), key, value);
break;
default:
invariant(false, "Invalid invariantOption " + invariantOptionString);
}
if (!realm.neverCheckProperty(object, key)) realm.markPropertyAsChecked(object, key);
global.$DefineOwnProperty(
"__assumeDataProperty",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__assumeDataProperty",
"__assumeDataProperty",
3,
(context, [object, propertyName, value, invariantOptions]) => {
if (!realm.useAbstractInterpretation) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
}
realm.generator = undefined; // don't emit code during the following $Set call
// casting to due to Flow workaround above
(object: any).$Set(key, value, object);
realm.generator = generator;
if (object.intrinsicName) realm.rebuildObjectProperty(object, key, value, object.intrinsicName);
return context.$Realm.intrinsics.undefined;
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
let generator = realm.generator;
invariant(generator);
let key = To.ToStringPartial(realm, propertyName);
if (realm.emitConcreteModel) {
generator.emitConcreteModel(key, value);
} else if (realm.invariantLevel >= 1) {
let invariantOptionString = invariantOptions
? To.ToStringPartial(realm, invariantOptions)
: "FULL_INVARIANT";
switch (invariantOptionString) {
// checks (!property in object || object.property === undefined)
case "VALUE_DEFINED_INVARIANT":
generator.emitPropertyInvariant(object, key, value.mightBeUndefined() ? "PRESENT" : "DEFINED");
break;
case "SKIP_INVARIANT":
break;
case "FULL_INVARIANT":
generator.emitFullInvariant((object: any), key, value);
break;
default:
invariant(false, "Invalid invariantOption " + invariantOptionString);
}
if (!realm.neverCheckProperty(object, key)) realm.markPropertyAsChecked(object, key);
}
realm.generator = undefined; // don't emit code during the following $Set call
// casting to due to Flow workaround above
(object: any).$Set(key, value, object);
realm.generator = generator;
if (object.intrinsicName) realm.rebuildObjectProperty(object, key, value, object.intrinsicName);
return context.$Realm.intrinsics.undefined;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}
),
writable: true,
enumerable: false,
configurable: true,
})
);
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}
),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__IntrospectionError",
new PropertyDescriptor({
value: realm.intrinsics.__IntrospectionError,
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__IntrospectionError", {
value: realm.intrinsics.__IntrospectionError,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__isIntegral",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => {
return new BooleanValue(realm, value instanceof IntegralValue);
}),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__isIntegral", {
value: new NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => {
return new BooleanValue(realm, value instanceof IntegralValue);
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__describe",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__describe", "__describe", 1, (context, [value]) => {
return new StringValue(realm, describeValue(value));
}),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__describe", {
value: new NativeFunctionValue(realm, "global.__describe", "__describe", 1, (context, [value]) => {
return new StringValue(realm, describeValue(value));
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"__fatal",
new PropertyDescriptor({
value: new NativeFunctionValue(realm, "global.__fatal", "__fatal", 0, (context, []) => {
throw new FatalError();
}),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__fatal", {
value: new NativeFunctionValue(realm, "global.__fatal", "__fatal", 0, (context, []) => {
throw new FatalError();
}),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty("__eagerlyRequireModuleDependencies", {
value: new NativeFunctionValue(
realm,
"global.__eagerlyRequireModuleDependencies",
"__eagerlyRequireModuleDependencies",
1,
(context, [functionValue]) => {
if (!IsCallable(realm, functionValue) || !(functionValue instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be callable function");
let functionCall: void | ((thisArgument: Value, argumentsList: Array<Value>) => Value) = functionValue.$Call;
if (typeof functionCall !== "function") {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be directly callable");
global.$DefineOwnProperty(
"__eagerlyRequireModuleDependencies",
new PropertyDescriptor({
value: new NativeFunctionValue(
realm,
"global.__eagerlyRequireModuleDependencies",
"__eagerlyRequireModuleDependencies",
1,
(context, [functionValue]) => {
if (!IsCallable(realm, functionValue) || !(functionValue instanceof FunctionValue))
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be callable function");
let functionCall: void | ((thisArgument: Value, argumentsList: Array<Value>) => Value) = functionValue.$Call;
if (typeof functionCall !== "function") {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be directly callable");
}
let old = realm.eagerlyRequireModuleDependencies;
realm.eagerlyRequireModuleDependencies = true;
try {
return functionCall(realm.intrinsics.undefined, []);
} finally {
realm.eagerlyRequireModuleDependencies = old;
}
}
let old = realm.eagerlyRequireModuleDependencies;
realm.eagerlyRequireModuleDependencies = true;
try {
return functionCall(realm.intrinsics.undefined, []);
} finally {
realm.eagerlyRequireModuleDependencies = old;
}
}
),
writable: true,
enumerable: false,
configurable: true,
});
),
writable: true,
enumerable: false,
configurable: true,
})
);
}

View File

@ -12,17 +12,21 @@
import type { Realm } from "../../realm.js";
import initializeConsole from "../common/console.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
if (!realm.isCompatibleWith(realm.MOBILE_JSC_VERSION) && !realm.isCompatibleWith("mobile"))
global.$DefineOwnProperty("console", {
value: initializeConsole(realm),
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
"console",
new PropertyDescriptor({
value: initializeConsole(realm),
writable: true,
enumerable: false,
configurable: true,
})
);
for (let name of [
"document",
@ -58,11 +62,14 @@ export default function(realm: Realm): void {
"FileReader",
"XMLHttpRequest",
]) {
global.$DefineOwnProperty(name, {
value: realm.intrinsics.undefined,
writable: true,
enumerable: false,
configurable: true,
});
global.$DefineOwnProperty(
name,
new PropertyDescriptor({
value: realm.intrinsics.undefined,
writable: true,
enumerable: false,
configurable: true,
})
);
}
}

View File

@ -25,6 +25,7 @@ import { HasSomeCompatibleType } from "./has.js";
import { Create, Properties } from "../singletons.js";
import invariant from "../invariant.js";
import type { BabelNodeClassMethod } from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
// ECMA262 9.2.8
export function MakeConstructor(
@ -56,21 +57,31 @@ export function MakeConstructor(
prototype.originalConstructor = F;
// b. Perform ! DefinePropertyOrThrow(prototype, "constructor", PropertyDescriptor{[[Value]]: F, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: true }).
Properties.DefinePropertyOrThrow(realm, prototype, "constructor", {
value: F,
writable: writablePrototype,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
prototype,
"constructor",
new PropertyDescriptor({
value: F,
writable: writablePrototype,
enumerable: false,
configurable: true,
})
);
}
// 6. Perform ! DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, F, "prototype", {
value: prototype,
writable: writablePrototype,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"prototype",
new PropertyDescriptor({
value: prototype,
writable: writablePrototype,
enumerable: false,
configurable: false,
})
);
// 7. Return NormalCompletion(undefined).
return realm.intrinsics.undefined;

View File

@ -38,6 +38,7 @@ import invariant from "../invariant.js";
import parse from "../utils/parse.js";
import traverseFast from "../utils/traverse-fast.js";
import type { BabelNodeIdentifier, BabelNodeLVal, BabelNodeFunctionDeclaration } from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
const allElementTypes = ["Undefined", "Null", "Boolean", "String", "Symbol", "Number", "Object"];
@ -69,12 +70,17 @@ export class CreateImplementation {
let length = value.value.length;
// 10. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
Properties.DefinePropertyOrThrow(realm, S, "length", {
value: new NumberValue(realm, length),
writable: false,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
S,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, length),
writable: false,
enumerable: false,
configurable: false,
})
);
// 11. Return S.
return S;
@ -307,12 +313,17 @@ export class CreateImplementation {
A.setExtensible(true);
// 10. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.OrdinaryDefineOwnProperty(realm, A, "length", {
value: new NumberValue(realm, length),
writable: true,
enumerable: false,
configurable: false,
});
Properties.OrdinaryDefineOwnProperty(
realm,
A,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, length),
writable: true,
enumerable: false,
configurable: false,
})
);
// 11. Return A.
return A;
@ -358,12 +369,17 @@ export class CreateImplementation {
// 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len,
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "length", {
value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
})
);
// 5. Let index be 0.
let index = 0;
@ -382,21 +398,31 @@ export class CreateImplementation {
// 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:
// %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, realm.intrinsics.SymbolIterator, {
value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
realm.intrinsics.SymbolIterator,
new PropertyDescriptor({
value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
})
);
// 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]:
// %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, obj, "callee", {
get: realm.intrinsics.ThrowTypeError,
set: realm.intrinsics.ThrowTypeError,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
"callee",
new PropertyDescriptor({
get: realm.intrinsics.ThrowTypeError,
set: realm.intrinsics.ThrowTypeError,
enumerable: false,
configurable: false,
})
);
// 10. Return obj.
return obj;
@ -442,7 +468,7 @@ export class CreateImplementation {
obj.setExtensible(true);
// 12. Let map be ObjectCreate(null).
let map = new ObjectValue(realm);
let map: ObjectValue = new ObjectValue(realm);
// 13. Set the [[ParameterMap]] internal slot of obj to map.
obj.$ParameterMap = map;
@ -473,12 +499,17 @@ export class CreateImplementation {
// 18. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len,
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "length", {
value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
})
);
// 19. Let mappedNames be an empty List.
let mappedNames = [];
@ -506,12 +537,15 @@ export class CreateImplementation {
// 3. Perform map.[[DefineOwnProperty]](! ToString(index), PropertyDescriptor{[[Set]]: p, [[Get]]: g,
// [[Enumerable]]: false, [[Configurable]]: true}).
map.$DefineOwnProperty(new StringValue(realm, index + ""), {
set: p,
get: g,
enumerable: false,
configurable: true,
});
map.$DefineOwnProperty(
new StringValue(realm, index + ""),
new PropertyDescriptor({
set: p,
get: g,
enumerable: false,
configurable: true,
})
);
}
}
@ -521,21 +555,31 @@ export class CreateImplementation {
// 22. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:
// %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, realm.intrinsics.SymbolIterator, {
value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
realm.intrinsics.SymbolIterator,
new PropertyDescriptor({
value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
})
);
// 23. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Value]]:
// func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "callee", {
value: func,
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
"callee",
new PropertyDescriptor({
value: func,
writable: true,
enumerable: false,
configurable: true,
})
);
// 24. Return obj.
return obj;
@ -549,12 +593,12 @@ export class CreateImplementation {
invariant(IsPropertyKey(realm, P), "Not a property key");
// 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
let newDesc = {
let newDesc = new PropertyDescriptor({
value: V,
writable: true,
enumerable: true,
configurable: true,
};
});
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
return O.$DefineOwnProperty(P, newDesc);
@ -601,7 +645,7 @@ export class CreateImplementation {
let desc = from.$GetOwnProperty(nextKey);
// If desc is not undefined and desc.[[Enumerable]] is true, then
if (desc !== undefined && desc.enumerable === true) {
if (desc !== undefined && desc.throwIfNotConcrete(realm).enumerable === true) {
// Let propValue be ? Get(from, nextKey).
let propValue = Get(realm, from, nextKey);
// Perform ! CreateDataProperty(target, nextKey, propValue).
@ -624,12 +668,12 @@ export class CreateImplementation {
invariant(IsPropertyKey(realm, P), "Not a property key");
// 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let newDesc = {
let newDesc = new PropertyDescriptor({
value: V,
writable: true,
enumerable: false,
configurable: true,
};
});
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
return O.$DefineOwnProperty(P, newDesc);
@ -903,12 +947,17 @@ export class CreateImplementation {
prototype.originalConstructor = F;
// b. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, F, "prototype", {
value: prototype,
writable: true,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"prototype",
new PropertyDescriptor({
value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
} else {
// 28. Else, perform MakeConstructor(F).
MakeConstructor(realm, F);

View File

@ -1,68 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow strict-local */
import type { Descriptor } from "../types.js";
export function cloneDescriptor(d: void | Descriptor): void | Descriptor {
if (d === undefined) return undefined;
let clone = {};
copyDescriptor(d, clone);
return clone;
}
export function clearDescriptor(d: Descriptor): void {
delete d.writable;
delete d.enumerable;
delete d.configurable;
delete d.value;
delete d.get;
delete d.set;
}
export function copyDescriptor(from: Descriptor, to: Descriptor): void {
if (from.hasOwnProperty("writable")) to.writable = from.writable;
if (from.hasOwnProperty("enumerable")) to.enumerable = from.enumerable;
if (from.hasOwnProperty("configurable")) to.configurable = from.configurable;
if (from.hasOwnProperty("value")) {
if (from.value instanceof Array) to.value = from.value.slice();
else to.value = from.value;
}
if (from.hasOwnProperty("get")) to.get = from.get;
if (from.hasOwnProperty("set")) to.set = from.set;
}
// does not check if the contents of value properties are the same
export function equalDescriptors(d1: Descriptor, d2: Descriptor): boolean {
if (d1.hasOwnProperty("writable")) {
if (!d2.hasOwnProperty("writable")) return false;
if (d1.writable !== d2.writable) return false;
}
if (d1.hasOwnProperty("enumerable")) {
if (!d2.hasOwnProperty("enumerable")) return false;
if (d1.enumerable !== d2.enumerable) return false;
}
if (d1.hasOwnProperty("configurable")) {
if (!d2.hasOwnProperty("configurable")) return false;
if (d1.configurable !== d2.configurable) return false;
}
if (d1.hasOwnProperty("value")) {
if (!d2.hasOwnProperty("value")) return false;
}
if (d1.hasOwnProperty("get")) {
if (!d2.hasOwnProperty("get")) return false;
if (d1.get !== d2.get) return false;
}
if (d1.hasOwnProperty("set")) {
if (!d2.hasOwnProperty("set")) return false;
if (d1.set !== d2.set) return false;
}
return true;
}

View File

@ -68,6 +68,7 @@ import type {
BabelNodeWithStatement,
} from "@babel/types";
import * as t from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
function InternalCall(
realm: Realm,
@ -756,12 +757,17 @@ export class FunctionImplementation {
}
// 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
return Properties.DefinePropertyOrThrow(realm, F, "name", {
value: name,
enumerable: false,
writable: false,
configurable: true,
});
return Properties.DefinePropertyOrThrow(
realm,
F,
"name",
new PropertyDescriptor({
value: name,
enumerable: false,
writable: false,
configurable: true,
})
);
}
// ECMA262 9.2.3
@ -792,22 +798,32 @@ export class FunctionImplementation {
}
// 3. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, F, "length", {
value: new NumberValue(realm, len),
writable: false,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"length",
new PropertyDescriptor({
value: new NumberValue(realm, len),
writable: false,
enumerable: false,
configurable: true,
})
);
// 4. Let Strict be the value of the [[Strict]] internal slot of F.
let Strict = F.$Strict;
if (!Strict) {
Properties.DefinePropertyOrThrow(realm, F, "caller", {
value: new UndefinedValue(realm),
writable: true,
enumerable: false,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"caller",
new PropertyDescriptor({
value: new UndefinedValue(realm),
writable: true,
enumerable: false,
configurable: true,
})
);
}
// 5. Set the [[Environment]] internal slot of F to the value of Scope.
@ -860,12 +876,12 @@ export class FunctionImplementation {
let thrower = realm.intrinsics.ThrowTypeError;
invariant(thrower);
let desc = {
let desc = new PropertyDescriptor({
get: thrower,
set: thrower,
enumerable: false,
configurable: true,
};
});
// 3. Perform ! DefinePropertyOrThrow(F, "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, F, "caller", desc);
// 4. Return ! DefinePropertyOrThrow(F, "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: true}).
@ -1222,12 +1238,17 @@ export class FunctionImplementation {
// Note: "arguments" ***MUST NOT*** be set if the function is in strict mode or is an arrow, method, constructor, or generator function.
// See 16.2 "Forbidden Extensions"
if (!Strict && kind === "normal") {
Properties.DefinePropertyOrThrow(realm, F, "arguments", {
value: realm.intrinsics.undefined,
enumerable: false,
writable: true,
configurable: true,
});
Properties.DefinePropertyOrThrow(
realm,
F,
"arguments",
new PropertyDescriptor({
value: realm.intrinsics.undefined,
enumerable: false,
writable: true,
configurable: true,
})
);
}
// 5. Return FunctionInitialize(F, kind, ParameterList, Body, Scope).

View File

@ -42,6 +42,7 @@ import { Create, Environment, Join, Leak, Path, To } from "../singletons.js";
import invariant from "../invariant.js";
import type { BabelNodeTemplateLiteral } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
// ECMA262 7.3.22
export function GetFunctionRealm(realm: Realm, obj: ObjectValue): Realm {
@ -97,7 +98,10 @@ export function OrdinaryGet(
let prop = O.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined && O.$GetOwnProperty(P) === undefined) {
let desc = prop.descriptor;
invariant(desc !== undefined);
invariant(
desc instanceof PropertyDescriptor,
"unknown properties are only created with Set and have equal descriptors"
);
let val = desc.value;
invariant(val instanceof AbstractValue);
let propValue;
@ -129,7 +133,7 @@ export function OrdinaryGet(
// 2. Let desc be ? O.[[GetOwnProperty]](P).
let desc = O.$GetOwnProperty(P);
if (desc === undefined || desc.joinCondition === undefined) return OrdinaryGetHelper();
if (desc === undefined || !(desc instanceof AbstractJoinedDescriptor)) return OrdinaryGetHelper();
// joined descriptors need special treatment
let joinCondition = desc.joinCondition;
@ -422,6 +426,10 @@ export function OrdinaryGetPartial(
if (prop !== undefined) {
let desc = prop.descriptor;
if (desc !== undefined) {
invariant(
desc instanceof PropertyDescriptor,
"unknown properties are only created with Set and have equal descriptors"
);
let val = desc.value;
invariant(val instanceof AbstractValue);
if (val.kind === "widened numeric property") {
@ -437,6 +445,7 @@ export function OrdinaryGetPartial(
for (let [key, propertyBinding] of O.properties) {
let desc = propertyBinding.descriptor;
if (desc === undefined) continue; // deleted
desc = desc.throwIfNotConcrete(realm); // TODO: Join descriptor values based on condition
invariant(desc.value !== undefined); // otherwise this is not simple
let val = desc.value;
invariant(val instanceof Value);
@ -717,23 +726,29 @@ export function GetTemplateObject(realm: Realm, templateLiteral: BabelNodeTempla
let cookedValue = new StringValue(realm, cookedStrings[index]);
// c. Call template.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: cookedValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
template.$DefineOwnProperty(prop, {
value: cookedValue,
writable: false,
enumerable: true,
configurable: false,
});
template.$DefineOwnProperty(
prop,
new PropertyDescriptor({
value: cookedValue,
writable: false,
enumerable: true,
configurable: false,
})
);
// d. Let rawValue be the String value rawStrings[index].
let rawValue = new StringValue(realm, rawStrings[index]);
// e. Call rawObj.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: rawValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
rawObj.$DefineOwnProperty(prop, {
value: rawValue,
writable: false,
enumerable: true,
configurable: false,
});
rawObj.$DefineOwnProperty(
prop,
new PropertyDescriptor({
value: rawValue,
writable: false,
enumerable: true,
configurable: false,
})
);
// f. Let index be index+1.
index = index + 1;
@ -743,12 +758,15 @@ export function GetTemplateObject(realm: Realm, templateLiteral: BabelNodeTempla
SetIntegrityLevel(realm, rawObj, "frozen");
// 12. Call template.[[DefineOwnProperty]]("raw", PropertyDescriptor{[[Value]]: rawObj, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}).
template.$DefineOwnProperty("raw", {
value: rawObj,
writable: false,
enumerable: false,
configurable: false,
});
template.$DefineOwnProperty(
"raw",
new PropertyDescriptor({
value: rawObj,
writable: false,
enumerable: false,
configurable: false,
})
);
// 13. Perform SetIntegrityLevel(template, "frozen").
SetIntegrityLevel(realm, template, "frozen");
@ -781,7 +799,7 @@ export function GetFromArrayWithWidenedNumericProperty(
if (prototypeBinding !== undefined) {
let descriptor = prototypeBinding.descriptor;
// ensure we are accessing a built-in native function
if (descriptor !== undefined && descriptor.value instanceof NativeFunctionValue) {
if (descriptor instanceof PropertyDescriptor && descriptor.value instanceof NativeFunctionValue) {
return descriptor.value;
}
}

View File

@ -82,7 +82,7 @@ export function HasOwnProperty(realm: Realm, O: ObjectValue | AbstractObjectValu
// 4. If desc is undefined, return false.
if (desc === undefined) return false;
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
Properties.ThrowIfMightHaveBeenDeleted(desc);
// 5. Return true.
return true;
@ -98,7 +98,7 @@ export function OrdinaryHasProperty(realm: Realm, O: ObjectValue, P: PropertyKey
// 3. If hasOwn is not undefined, return true.
if (hasOwn !== undefined) {
Properties.ThrowIfMightHaveBeenDeleted(hasOwn.value);
Properties.ThrowIfMightHaveBeenDeleted(hasOwn);
return true;
}

View File

@ -13,7 +13,6 @@ export * from "./abstract.js";
export * from "./call.js";
export * from "./construct.js";
export * from "./date.js";
export * from "./descriptor.js";
export * from "./get.js";
export * from "./has.js";
export * from "./hash.js";

View File

@ -15,6 +15,7 @@ import { IsExtensible, IsDataDescriptor, IsAccessorDescriptor } from "./index.js
import { Properties } from "../singletons.js";
import { FatalError } from "../errors.js";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
type IntegrityLevels = "sealed" | "frozen";
@ -54,9 +55,14 @@ export function SetIntegrityLevel(realm: Realm, O: ObjectValue, level: Integrity
// a. Repeat for each element k of keys,
for (let k of keys) {
// i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor{[[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, O, k, {
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
O,
k,
new PropertyDescriptor({
configurable: false,
})
);
}
} else if (level === "frozen") {
// 7. Else level is "frozen",
@ -67,17 +73,17 @@ export function SetIntegrityLevel(realm: Realm, O: ObjectValue, level: Integrity
// ii. If currentDesc is not undefined, then
if (currentDesc) {
Properties.ThrowIfMightHaveBeenDeleted(currentDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(currentDesc);
let desc;
// 1. If IsAccessorDescriptor(currentDesc) is true, then
if (IsAccessorDescriptor(realm, currentDesc)) {
// a. Let desc be the PropertyDescriptor{[[Configurable]]: false}.
desc = { configurable: false };
desc = new PropertyDescriptor({ configurable: false });
} else {
// 2. Else,
// b. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
desc = { configurable: false, writable: false };
desc = new PropertyDescriptor({ configurable: false, writable: false });
}
// 3. Perform ? DefinePropertyOrThrow(O, k, desc).
@ -116,7 +122,8 @@ export function TestIntegrityLevel(realm: Realm, O: ObjectValue, level: Integrit
// b. If currentDesc is not undefined, then
if (currentDesc) {
Properties.ThrowIfMightHaveBeenDeleted(currentDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(currentDesc);
currentDesc = currentDesc.throwIfNotConcrete(realm);
// i. If currentDesc.[[Configurable]] is true, return false.
if (currentDesc.configurable === true) return false;

View File

@ -31,6 +31,7 @@ import { Value } from "../values/index.js";
import invariant from "../invariant.js";
import { HasName, HasCompatibleType } from "./has.js";
import type { BabelNodeExpression, BabelNodeCallExpression, BabelNodeLVal, BabelNodeClassMethod } from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
// ECMA262 22.1.3.1.1
export function IsConcatSpreadable(realm: Realm, _O: Value): boolean {
@ -51,7 +52,7 @@ export function IsConcatSpreadable(realm: Realm, _O: Value): boolean {
}
// ECMA262 6.2.4.3
export function IsGenericDescriptor(realm: Realm, Desc: ?Descriptor): boolean {
function IsGenericDescriptorInternal(realm: Realm, Desc: ?Descriptor): boolean {
// 1. If Desc is undefined, return false.
if (!Desc) return false;
@ -63,29 +64,47 @@ export function IsGenericDescriptor(realm: Realm, Desc: ?Descriptor): boolean {
}
// ECMA262 6.2.4.1
export function IsAccessorDescriptor(realm: Realm, Desc: ?Descriptor): boolean {
function IsAccessorDescriptorInternal(realm: Realm, Desc: ?Descriptor): boolean {
// 1. If Desc is undefined, return false.
if (!Desc) return false;
// 2. If both Desc.[[Get]] and Desc.[[Set]] are absent, return false.
if (!("get" in Desc) && !("set" in Desc)) return false;
Desc = Desc.throwIfNotConcrete(realm);
if (Desc.get === undefined && Desc.set === undefined) return false;
// 3. Return true.
return true;
}
// ECMA262 6.2.4.2
export function IsDataDescriptor(realm: Realm, Desc: ?Descriptor): boolean {
function IsDataDescriptorInternal(realm: Realm, Desc: ?Descriptor): boolean {
// If Desc is undefined, return false.
if (!Desc) return false;
// If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false.
if (!("value" in Desc) && !("writable" in Desc)) return false;
Desc = Desc.throwIfNotConcrete(realm);
if (Desc.value === undefined && Desc.writable === undefined) return false;
// Return true.
return true;
}
// Flow wrappers that provide refinements using Predicate Functions.
// These wrappers also assert that the type is PropertyDescriptor so that if this returns
// true, then Flow can refine that the type of Desc as PropertyDescriptor.
export function IsGenericDescriptor(realm: Realm, Desc: ?Descriptor): boolean %checks {
return IsGenericDescriptorInternal(realm, Desc) && Desc instanceof PropertyDescriptor;
}
export function IsAccessorDescriptor(realm: Realm, Desc: ?Descriptor): boolean %checks {
return IsAccessorDescriptorInternal(realm, Desc) && Desc instanceof PropertyDescriptor;
}
export function IsDataDescriptor(realm: Realm, Desc: ?Descriptor): boolean %checks {
return IsDataDescriptorInternal(realm, Desc) && Desc instanceof PropertyDescriptor;
}
// ECMA262 9.1.3.1
export function OrdinaryIsExtensible(realm: Realm, O: ObjectValue): boolean {
// 1. Return the value of the [[Extensible]] internal slot of O.

View File

@ -13,6 +13,13 @@ import type { Binding } from "../environment.js";
import type { Bindings, BindingEntry, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
import { construct_empty_effects, Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js";
import {
cloneDescriptor,
equalDescriptors,
PropertyDescriptor,
AbstractJoinedDescriptor,
InternalSlotDescriptor,
} from "../descriptors.js";
import {
AbruptCompletion,
@ -26,7 +33,7 @@ import {
ReturnCompletion,
ThrowCompletion,
} from "../completions.js";
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
import { IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
import { Path } from "../singletons.js";
import { Generator } from "../utils/generator.js";
import { AbstractValue, ConcreteValue, EmptyValue, Value } from "../values/index.js";
@ -366,7 +373,7 @@ export class JoinImplementation {
if (c2.has(b.object)) return d2; // no join
if (b.descriptor !== undefined && m1.has(b)) {
// property was deleted
d1 = cloneDescriptor(b.descriptor);
d1 = cloneDescriptor(b.descriptor.throwIfNotConcrete(realm));
invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty;
} else {
@ -378,7 +385,7 @@ export class JoinImplementation {
if (c1.has(b.object)) return d1; // no join
if (b.descriptor !== undefined && m2.has(b)) {
// property was deleted
d2 = cloneDescriptor(b.descriptor);
d2 = cloneDescriptor(b.descriptor.throwIfNotConcrete(realm));
invariant(d2 !== undefined);
d2.value = realm.intrinsics.empty;
} else {
@ -403,69 +410,79 @@ export class JoinImplementation {
let clone_with_abstract_value = (d: Descriptor) => {
invariant(d === d1 || d === d2);
if (!IsDataDescriptor(realm, d)) {
let d3: Descriptor = {};
d3.joinCondition = joinCondition;
return d3;
return new AbstractJoinedDescriptor(joinCondition);
}
let dc = cloneDescriptor(d);
invariant(dc !== undefined);
let dcValue = dc.value;
if (Array.isArray(dcValue)) {
invariant(dcValue.length > 0);
let elem0 = dcValue[0];
if (elem0 instanceof Value) {
dc.value = dcValue.map(e => {
return d === d1
? getAbstractValue((e: any), realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, (e: any));
});
} else {
dc.value = dcValue.map(e => {
let { $Key: key1, $Value: val1 } = (e: any);
let key3 =
d === d1
? getAbstractValue(key1, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, key1);
let val3 =
d === d1
? getAbstractValue(val1, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, val1);
return { $Key: key3, $Value: val3 };
});
let dc;
let dcValue;
if (d instanceof InternalSlotDescriptor) {
dc = new InternalSlotDescriptor(d.value);
dcValue = dc.value;
if (Array.isArray(dcValue)) {
invariant(dcValue.length > 0);
let elem0 = dcValue[0];
if (elem0 instanceof Value) {
dc.value = dcValue.map(e => {
return d === d1
? getAbstractValue((e: any), realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, (e: any));
});
} else {
dc.value = dcValue.map(e => {
let { $Key: key1, $Value: val1 } = (e: any);
let key3 =
d === d1
? getAbstractValue(key1, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, key1);
let val3 =
d === d1
? getAbstractValue(val1, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, val1);
return { $Key: key3, $Value: val3 };
});
}
}
} else {
invariant(dcValue === undefined || dcValue instanceof Value);
dc.value =
d === d1
? getAbstractValue(dcValue, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, dcValue);
dc = cloneDescriptor(d.throwIfNotConcrete(realm));
invariant(dc !== undefined);
dcValue = dc.value;
}
invariant(dcValue === undefined || dcValue instanceof Value);
dc.value =
d === d1
? getAbstractValue(dcValue, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, dcValue);
return dc;
};
if (d1 === undefined) {
if (d2 === undefined) return undefined;
// d2 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d2);
if (!IsDataDescriptor(realm, d2)) d3.descriptor2 = d2;
if (d3 instanceof AbstractJoinedDescriptor) d3.descriptor2 = d2;
return d3;
} else if (d2 === undefined) {
invariant(d1 !== undefined);
// d1 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d1);
if (!IsDataDescriptor(realm, d1)) d3.descriptor1 = d1;
if (d3 instanceof AbstractJoinedDescriptor) d3.descriptor1 = d1;
return d3;
} else {
if (equalDescriptors(d1, d2) && IsDataDescriptor(realm, d1)) {
if (
d1 instanceof PropertyDescriptor &&
d2 instanceof PropertyDescriptor &&
equalDescriptors(d1, d2) &&
IsDataDescriptor(realm, d1)
) {
let dc = cloneDescriptor(d1);
invariant(dc !== undefined);
dc.value = this.joinValues(realm, d1.value, d2.value, getAbstractValue);
let dcValue = this.joinValues(realm, d1.value, d2.value, getAbstractValue);
invariant(dcValue instanceof Value);
dc.value = dcValue;
return dc;
}
let d3: Descriptor = {};
d3.joinCondition = joinCondition;
d3.descriptor1 = d1;
d3.descriptor2 = d2;
return d3;
if (d1 instanceof InternalSlotDescriptor && d2 instanceof InternalSlotDescriptor) {
return new InternalSlotDescriptor(this.joinValues(realm, d1.value, d2.value, getAbstractValue));
}
return new AbstractJoinedDescriptor(joinCondition, d1, d2);
}
}

View File

@ -100,8 +100,8 @@ export function EnumerableOwnProperties(
let desc = O.$GetOwnProperty(key);
// ii. If desc is not undefined and desc.[[Enumerable]] is true, then
if (desc && desc.enumerable) {
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc && desc.throwIfNotConcrete(realm).enumerable) {
Properties.ThrowIfMightHaveBeenDeleted(desc);
// 1. If kind is "key", append key to properties.
if (kind === "key") {

View File

@ -32,8 +32,6 @@ import { CompilerDiagnostic, FatalError } from "../errors.js";
import invariant from "../invariant.js";
import {
Call,
cloneDescriptor,
equalDescriptors,
Get,
GetGlobalObject,
GetThisValue,
@ -53,6 +51,7 @@ import { Create, Environment, Functions, Leak, Join, Path, To } from "../singlet
import IsStrict from "../utils/strict.js";
import { createOperationDescriptor } from "../utils/generator.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import { cloneDescriptor, equalDescriptors, PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
function StringKey(key: PropertyKeyValue): string {
if (key instanceof StringValue) key = key.value;
@ -123,12 +122,12 @@ function InternalUpdatedProperty(realm: Realm, O: ObjectValue, P: PropertyKeyVal
generator.emitPropertyDelete(O, P);
}
} else {
let descValue = desc.value || realm.intrinsics.undefined;
invariant(descValue instanceof Value);
desc = desc.throwIfNotConcrete(realm);
if (oldDesc === undefined) {
// The property is being created
if (O === realm.$GlobalObject) {
if (IsDataDescriptor(realm, desc)) {
let descValue = desc.value || realm.intrinsics.undefined;
if (isValidIdentifier(P) && !desc.configurable && desc.enumerable && desc.writable) {
generator.emitGlobalDeclaration(P, descValue);
} else if (desc.configurable && desc.enumerable && desc.writable) {
@ -141,14 +140,18 @@ function InternalUpdatedProperty(realm: Realm, O: ObjectValue, P: PropertyKeyVal
}
} else {
if (IsDataDescriptor(realm, desc) && desc.configurable && desc.enumerable && desc.writable) {
let descValue = desc.value || realm.intrinsics.undefined;
generator.emitPropertyAssignment(O, P, descValue);
} else {
generator.emitDefineProperty(O, P, desc);
}
}
} else {
invariant(oldDesc instanceof PropertyDescriptor);
// The property is being modified
if (equalDescriptors(desc, oldDesc)) {
invariant(IsDataDescriptor(realm, desc));
let descValue = desc.value || realm.intrinsics.undefined;
// only the value is being modified
if (O === realm.$GlobalObject) {
generator.emitGlobalAssignment(P, descValue);
@ -163,6 +166,16 @@ function InternalUpdatedProperty(realm: Realm, O: ObjectValue, P: PropertyKeyVal
}
function leakDescriptor(realm: Realm, desc: Descriptor) {
if (desc instanceof AbstractJoinedDescriptor) {
if (desc.descriptor1) {
leakDescriptor(realm, desc.descriptor1);
}
if (desc.descriptor2) {
leakDescriptor(realm, desc.descriptor2);
}
}
invariant(desc instanceof PropertyDescriptor);
if (desc.value) {
if (desc.value instanceof Value) Leak.value(realm, desc.value);
else if (desc.value !== undefined) {
@ -187,14 +200,7 @@ function parentPermitsChildPropertyCreation(realm: Realm, O: ObjectValue, P: Pro
}
let ownDesc = O.$GetOwnProperty(P);
let ownDescValue = !ownDesc
? realm.intrinsics.undefined
: ownDesc.value === undefined
? realm.intrinsics.undefined
: ownDesc.value;
invariant(ownDescValue instanceof Value);
if (!ownDesc || ownDescValue.mightHaveBeenDeleted()) {
if (!ownDesc || ownDesc.mightHaveBeenDeleted()) {
// O might not object, so first ask its parent
let parent = O.$GetPrototypeOf();
if (!(parent instanceof NullValue)) {
@ -268,15 +274,9 @@ export class PropertiesImplementation {
// 2. Let ownDesc be ? O.[[GetOwnProperty]](P).
let ownDesc = O.$GetOwnProperty(P);
let ownDescValue = !ownDesc
? realm.intrinsics.undefined
: ownDesc.value === undefined
? realm.intrinsics.undefined
: ownDesc.value;
invariant(ownDescValue instanceof Value);
// 3. If ownDesc is undefined (or might be), then
if (!ownDesc || ownDescValue.mightHaveBeenDeleted()) {
if (!ownDesc || ownDesc.mightHaveBeenDeleted()) {
// a. Let parent be ? O.[[GetPrototypeOf]]().
let parent = O.$GetPrototypeOf();
@ -290,9 +290,17 @@ export class PropertiesImplementation {
// But since we don't know if O has its own property P, the parent might
// actually have a say. Give up, unless the parent would be OK with it.
if (!parentPermitsChildPropertyCreation(realm, parent, P)) {
invariant(ownDescValue instanceof AbstractValue);
AbstractValue.reportIntrospectionError(ownDescValue);
throw new FatalError();
// TODO: Join the effects depending on if the property was deleted or not.
let error = new CompilerDiagnostic(
"assignment might or might not invoke a setter",
realm.currentLocation,
"PP0043",
"RecoverableError"
);
if (realm.handleError(error) !== "Recover") {
throw new FatalError();
}
// If we recover, we assume that the parent would've been fine creating the property.
}
// Since the parent is OK with us creating a local property for O
// we can carry on as if there were no parent.
@ -300,17 +308,17 @@ export class PropertiesImplementation {
// i. Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
if (!ownDesc)
ownDesc = ({
ownDesc = new PropertyDescriptor({
value: realm.intrinsics.undefined,
writable: true,
enumerable: true,
configurable: true,
}: any);
});
}
// joined descriptors need special treatment
let joinCondition = ownDesc.joinCondition;
if (joinCondition !== undefined) {
if (ownDesc instanceof AbstractJoinedDescriptor) {
let joinCondition = ownDesc.joinCondition;
let descriptor2 = ownDesc.descriptor2;
ownDesc = ownDesc.descriptor1;
let e1 = Path.withCondition(joinCondition, () => {
@ -354,18 +362,25 @@ export class PropertiesImplementation {
function OrdinarySetHelper(): boolean {
invariant(ownDesc !== undefined);
invariant(ownDescValue instanceof Value);
// 4. If IsDataDescriptor(ownDesc) is true, then
if (IsDataDescriptor(realm, ownDesc)) {
// a. If ownDesc.[[Writable]] is false, return false.
if (!ownDesc.writable && !weakDeletion) {
// The write will fail if the property actually exists
if (ownDescValue.mightHaveBeenDeleted()) {
if (ownDesc.value && ownDesc.value.mightHaveBeenDeleted()) {
// But maybe it does not and thus would succeed.
// Since we don't know what will happen, give up for now.
invariant(ownDescValue instanceof AbstractValue);
AbstractValue.reportIntrospectionError(ownDescValue);
throw new FatalError();
// TODO: Join the effects depending on if the property was deleted or not.
let error = new CompilerDiagnostic(
"assignment might or might not invoke a setter",
realm.currentLocation,
"PP0043",
"RecoverableError"
);
if (realm.handleError(error) !== "Recover") {
throw new FatalError();
}
// If we recover we assume that the property was there.
}
return false;
}
@ -376,7 +391,7 @@ export class PropertiesImplementation {
// c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
let existingDescriptor = Receiver.$GetOwnProperty(P);
if (existingDescriptor !== undefined) {
if (existingDescriptor instanceof AbstractJoinedDescriptor) {
if (existingDescriptor.descriptor1 === ownDesc) existingDescriptor = ownDesc;
else if (existingDescriptor.descriptor2 === ownDesc) existingDescriptor = ownDesc;
}
@ -410,7 +425,7 @@ export class PropertiesImplementation {
}
// iii. Let valueDesc be the PropertyDescriptor{[[Value]]: V}.
let valueDesc = { value: V };
let valueDesc = new PropertyDescriptor({ value: V });
// iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
if (weakDeletion || existingDescValue.mightHaveBeenDeleted()) {
@ -420,7 +435,7 @@ export class PropertiesImplementation {
// change the attributes of the property, so we can reuse the existing
// descriptor.
valueDesc = existingDescriptor;
valueDesc.value = V;
valueDesc.throwIfNotConcrete(realm).value = V;
}
return Receiver.$DefineOwnProperty(P, valueDesc);
} else {
@ -435,7 +450,7 @@ export class PropertiesImplementation {
invariant(IsAccessorDescriptor(realm, ownDesc), "expected accessor");
// 6. Let setter be ownDesc.[[Set]].
let setter = "set" in ownDesc ? ownDesc.set : undefined;
let setter = ownDesc.set;
// 7. If setter is undefined, return false.
if (!setter || setter instanceof UndefinedValue) return false;
@ -569,16 +584,20 @@ export class PropertiesImplementation {
]);
newVal = AbstractValue.createFromConditionalOp(realm, cond, V, sentinel);
}
prop.descriptor = {
prop.descriptor = new PropertyDescriptor({
writable: true,
enumerable: true,
configurable: true,
value: newVal,
};
});
} else {
invariant(
desc instanceof PropertyDescriptor,
"unknown properties are only created with Set and have equal descriptors"
);
// join V with current value of O.unknownProperty. I.e. weak update.
let oldVal = desc.value;
invariant(oldVal instanceof Value);
invariant(oldVal);
let newVal = oldVal;
if (!(V instanceof UndefinedValue)) {
if (isWidenedValue(P)) {
@ -604,9 +623,11 @@ export class PropertiesImplementation {
continue;
}
let oldVal = realm.intrinsics.empty;
if (propertyBinding.descriptor && propertyBinding.descriptor.value) {
oldVal = propertyBinding.descriptor.value;
invariant(oldVal instanceof Value); // otherwise this is not simple
if (propertyBinding.descriptor) {
let d = propertyBinding.descriptor.throwIfNotConcrete(realm);
if (d.value) {
oldVal = d.value;
}
}
let cond = AbstractValue.createFromBinaryOp(realm, "===", P, new StringValue(realm, key));
let newVal = AbstractValue.createFromConditionalOp(realm, cond, V, oldVal);
@ -622,7 +643,7 @@ export class PropertiesImplementation {
// 1. If Desc is undefined, return undefined.
if (!Desc) return realm.intrinsics.undefined;
if (Desc.joinCondition) {
if (Desc instanceof AbstractJoinedDescriptor) {
return AbstractValue.createFromConditionalOp(
realm,
Desc.joinCondition,
@ -630,6 +651,7 @@ export class PropertiesImplementation {
this.FromPropertyDescriptor(realm, Desc.descriptor2)
);
}
invariant(Desc instanceof PropertyDescriptor);
// 2. Let obj be ObjectCreate(%ObjectPrototype%).
let obj = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
@ -640,44 +662,38 @@ export class PropertiesImplementation {
// 4. If Desc has a [[Value]] field, then
let success = true;
if ("value" in Desc) {
invariant(Desc.value instanceof Value);
if (Desc.value !== undefined) {
// a. Perform CreateDataProperty(obj, "value", Desc.[[Value]]).
success = Create.CreateDataProperty(realm, obj, "value", Desc.value) && success;
}
// 5. If Desc has a [[Writable]] field, then
if ("writable" in Desc) {
invariant(Desc.writable !== undefined);
if (Desc.writable !== undefined) {
// a. Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]).
success = Create.CreateDataProperty(realm, obj, "writable", new BooleanValue(realm, Desc.writable)) && success;
}
// 6. If Desc has a [[Get]] field, then
if ("get" in Desc) {
invariant(Desc.get !== undefined);
if (Desc.get !== undefined) {
// a. Perform CreateDataProperty(obj, "get", Desc.[[Get]]).
success = Create.CreateDataProperty(realm, obj, "get", Desc.get) && success;
}
// 7. If Desc has a [[Set]] field, then
if ("set" in Desc) {
invariant(Desc.set !== undefined);
if (Desc.set !== undefined) {
// a. Perform CreateDataProperty(obj, "set", Desc.[[Set]]).
success = Create.CreateDataProperty(realm, obj, "set", Desc.set) && success;
}
// 8. If Desc has an [[Enumerable]] field, then
if ("enumerable" in Desc) {
invariant(Desc.enumerable !== undefined);
if (Desc.enumerable !== undefined) {
// a. Perform CreateDataProperty(obj, "enumerable", Desc.[[Enumerable]]).
success =
Create.CreateDataProperty(realm, obj, "enumerable", new BooleanValue(realm, Desc.enumerable)) && success;
}
// 9. If Desc has a [[Configurable]] field, then
if ("configurable" in Desc) {
invariant(Desc.configurable !== undefined);
if (Desc.configurable !== undefined) {
// a. Perform CreateDataProperty(obj, "configurable", Desc.[[Configurable]]).
success =
Create.CreateDataProperty(realm, obj, "configurable", new BooleanValue(realm, Desc.configurable)) && success;
@ -709,6 +725,8 @@ export class PropertiesImplementation {
return true;
}
desc = desc.throwIfNotConcrete(realm);
// 4. If desc.[[Configurable]] is true, then
if (desc.configurable) {
ensureIsNotFinal(realm, O, P);
@ -765,8 +783,9 @@ export class PropertiesImplementation {
}
// ECMA262 6.2.4.6
CompletePropertyDescriptor(realm: Realm, Desc: Descriptor): Descriptor {
CompletePropertyDescriptor(realm: Realm, _Desc: Descriptor): Descriptor {
// 1. Assert: Desc is a Property Descriptor.
let Desc = _Desc.throwIfNotConcrete(realm);
// 2. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, [[Enumerable]]: false, [[Configurable]]: false}.
let like = {
@ -781,22 +800,22 @@ export class PropertiesImplementation {
// 3. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then
if (IsGenericDescriptor(realm, Desc) || IsDataDescriptor(realm, Desc)) {
// a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]].
if (!("value" in Desc)) Desc.value = like.value;
if (Desc.value === undefined) Desc.value = like.value;
// b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]].
if (!("writable" in Desc)) Desc.writable = like.writable;
if (Desc.writable === undefined) Desc.writable = like.writable;
} else {
// 4. Else,
// a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]].
if (!("get" in Desc)) Desc.get = like.get;
if (Desc.get === undefined) Desc.get = like.get;
// b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]].
if (!("set" in Desc)) Desc.set = like.set;
if (Desc.set === undefined) Desc.set = like.set;
}
// 5. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]].
if (!("enumerable" in Desc)) Desc.enumerable = like.enumerable;
if (Desc.enumerable === undefined) Desc.enumerable = like.enumerable;
// 6. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]].
if (!("configurable" in Desc)) Desc.configurable = like.configurable;
if (Desc.configurable === undefined) Desc.configurable = like.configurable;
// 7. Return Desc.
return Desc;
@ -814,16 +833,19 @@ export class PropertiesImplementation {
O: void | ObjectValue,
P: void | PropertyKeyValue,
extensible: boolean,
Desc: Descriptor,
current: ?Descriptor
_Desc: Descriptor,
_current: ?Descriptor
): boolean {
let Desc = _Desc;
let current = _current;
// 1. Assert: If O is not undefined, then IsPropertyKey(P) is true.
if (O !== undefined) {
invariant(P !== undefined);
invariant(IsPropertyKey(realm, P));
}
if (current && current.joinCondition !== undefined) {
if (current instanceof AbstractJoinedDescriptor) {
let jc = current.joinCondition;
if (Path.implies(jc)) current = current.descriptor1;
else if (!AbstractValue.createFromUnaryOp(realm, "!", jc, true).mightNotBeTrue()) current = current.descriptor2;
@ -842,7 +864,7 @@ export class PropertiesImplementation {
if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) {
leakDescriptor(realm, Desc);
if (realm.generator !== undefined) {
realm.generator.emitDefineProperty(O, StringKey(P), Desc);
realm.generator.emitDefineProperty(O, StringKey(P), Desc.throwIfNotConcrete(realm));
}
return true;
}
@ -856,12 +878,17 @@ export class PropertiesImplementation {
// to its default value.
if (O !== undefined) {
invariant(P !== undefined);
InternalSetProperty(realm, O, P, {
value: "value" in Desc ? Desc.value : realm.intrinsics.undefined,
writable: "writable" in Desc ? Desc.writable : false,
enumerable: "enumerable" in Desc ? Desc.enumerable : false,
configurable: "configurable" in Desc ? Desc.configurable : false,
});
InternalSetProperty(
realm,
O,
P,
new PropertyDescriptor({
value: Desc.value !== undefined ? Desc.value : realm.intrinsics.undefined,
writable: Desc.writable !== undefined ? Desc.writable : false,
enumerable: Desc.enumerable !== undefined ? Desc.enumerable : false,
configurable: Desc.configurable !== undefined ? Desc.configurable : false,
})
);
InternalUpdatedProperty(realm, O, P, undefined);
}
} else {
@ -872,12 +899,18 @@ export class PropertiesImplementation {
// default value.
if (O !== undefined) {
invariant(P !== undefined);
InternalSetProperty(realm, O, P, {
get: "get" in Desc ? Desc.get : realm.intrinsics.undefined,
set: "set" in Desc ? Desc.set : realm.intrinsics.undefined,
enumerable: "enumerable" in Desc ? Desc.enumerable : false,
configurable: "configurable" in Desc ? Desc.configurable : false,
});
Desc = Desc.throwIfNotConcrete(realm);
InternalSetProperty(
realm,
O,
P,
new PropertyDescriptor({
get: Desc.get !== undefined ? Desc.get : realm.intrinsics.undefined,
set: Desc.set !== undefined ? Desc.set : realm.intrinsics.undefined,
enumerable: Desc.enumerable !== undefined ? Desc.enumerable : false,
configurable: Desc.configurable !== undefined ? Desc.configurable : false,
})
);
InternalUpdatedProperty(realm, O, P, undefined);
}
}
@ -886,18 +919,31 @@ export class PropertiesImplementation {
return true;
}
current = current.throwIfNotConcrete(realm);
Desc = Desc.throwIfNotConcrete(realm);
// 3. Return true, if every field in Desc is absent.
if (!Object.keys(Desc).length) return true;
let allAbsent = true;
for (let field in Desc) {
if ((Desc: any)[field] !== undefined) {
allAbsent = false;
break;
}
}
if (allAbsent) return true;
// 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the
// same value as the corresponding field in current when compared using the SameValue algorithm.
let identical = true;
for (let field in Desc) {
if (!(field in current)) {
if ((Desc: any)[field] === undefined) {
continue;
}
if ((current: any)[field] === undefined) {
identical = false;
} else {
let dval = InternalDescriptorPropertyToValue(realm, Desc[field]);
let cval = InternalDescriptorPropertyToValue(realm, current[field]);
let dval = InternalDescriptorPropertyToValue(realm, (Desc: any)[field]);
let cval = InternalDescriptorPropertyToValue(realm, (current: any)[field]);
if (dval instanceof ConcreteValue && cval instanceof ConcreteValue) identical = SameValue(realm, dval, cval);
else {
identical = dval === cval;
@ -924,11 +970,14 @@ export class PropertiesImplementation {
if (Desc.configurable) return false;
// b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other.
if ("enumerable" in Desc && Desc.enumerable !== current.enumerable) {
if (Desc.enumerable !== undefined && Desc.enumerable !== current.enumerable) {
return false;
}
}
current = current.throwIfNotConcrete(realm);
Desc = Desc.throwIfNotConcrete(realm);
if (O !== undefined && P !== undefined) {
ensureIsNotFinal(realm, O, P);
if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) {
@ -957,8 +1006,8 @@ export class PropertiesImplementation {
// Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values.
if (O !== undefined) {
invariant(P !== undefined);
delete current.writable;
delete current.value;
current.writable = undefined;
current.value = undefined;
current.get = realm.intrinsics.undefined;
current.set = realm.intrinsics.undefined;
}
@ -967,8 +1016,8 @@ export class PropertiesImplementation {
// i. If O is not undefined, convert the property named P of object O from an accessor property to a data property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values.
if (O !== undefined) {
invariant(P !== undefined);
delete current.get;
delete current.set;
current.get = undefined;
current.set = undefined;
current.writable = false;
current.value = realm.intrinsics.undefined;
}
@ -1010,8 +1059,8 @@ export class PropertiesImplementation {
// If the property might have been deleted, we need to ensure that either
// the new descriptor overrides any existing values, or always results in
// the default value.
let unknownEnumerable = !("enumerable" in Desc) && !!current.enumerable;
let unknownWritable = !("writable" in Desc) && !!current.writable;
let unknownEnumerable = Desc.enumerable === undefined && !!current.enumerable;
let unknownWritable = Desc.writable === undefined && !!current.writable;
if (unknownEnumerable || unknownWritable) {
let error = new CompilerDiagnostic(
"unknown descriptor attributes on deleted property",
@ -1046,7 +1095,11 @@ export class PropertiesImplementation {
// a. For each field of Desc that is present, set the corresponding attribute of the property named P of
// object O to the value of the field.
for (let field in Desc) current[field] = Desc[field];
for (let field in Desc) {
if ((Desc: any)[field] !== undefined) {
(current: any)[field] = (Desc: any)[field];
}
}
InternalUpdatedProperty(realm, O, P, oldDesc);
}
@ -1092,8 +1145,8 @@ export class PropertiesImplementation {
let propDesc = props.$GetOwnProperty(nextKey);
// b. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then
if (propDesc && propDesc.enumerable) {
this.ThrowIfMightHaveBeenDeleted(propDesc.value);
if (propDesc && propDesc.throwIfNotConcrete(realm).enumerable) {
this.ThrowIfMightHaveBeenDeleted(propDesc);
// i. Let descObj be ? Get(props, nextKey).
let descObj = Get(realm, props, nextKey);
@ -1239,7 +1292,9 @@ export class PropertiesImplementation {
}
// ECMA262 9.4.2.4
ArraySetLength(realm: Realm, A: ArrayValue, Desc: Descriptor): boolean {
ArraySetLength(realm: Realm, A: ArrayValue, _Desc: Descriptor): boolean {
let Desc = _Desc.throwIfNotConcrete(realm);
// 1. If the [[Value]] field of Desc is absent, then
let DescValue = Desc.value;
if (!DescValue) {
@ -1249,7 +1304,7 @@ export class PropertiesImplementation {
invariant(DescValue instanceof Value);
// 2. Let newLenDesc be a copy of Desc.
let newLenDesc = Object.assign({}, Desc);
let newLenDesc = new PropertyDescriptor(Desc);
// 3. Let newLen be ? ToUint32(Desc.[[Value]]).
let newLen = To.ToUint32(realm, DescValue);
@ -1274,6 +1329,7 @@ export class PropertiesImplementation {
oldLenDesc !== undefined && !IsAccessorDescriptor(realm, oldLenDesc),
"cannot be undefined or an accessor descriptor"
);
oldLenDesc = oldLenDesc.throwIfNotConcrete(realm);
// 9. Let oldLen be oldLenDesc.[[Value]].
let oldLen = oldLenDesc.value;
@ -1293,7 +1349,7 @@ export class PropertiesImplementation {
// 12. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
let newWritable;
if (!("writable" in newLenDesc) || newLenDesc.writable === true) {
if (newLenDesc.writable === undefined || newLenDesc.writable === true) {
newWritable = true;
} else {
// 13. Else,
@ -1348,9 +1404,14 @@ export class PropertiesImplementation {
// 17. If newWritable is false, then
if (!newWritable) {
// a. Return OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Writable]]: false}). This call will always return true.
return this.OrdinaryDefineOwnProperty(realm, A, "length", {
writable: false,
});
return this.OrdinaryDefineOwnProperty(
realm,
A,
"length",
new PropertyDescriptor({
writable: false,
})
);
}
// 18. Return true.
@ -1382,7 +1443,7 @@ export class PropertiesImplementation {
{ 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 };
return new PropertyDescriptor({ configurable: true, enumerable: true, value: absVal, writable: true });
}
// 1. Assert: IsPropertyKey(P) is true.
@ -1443,7 +1504,7 @@ export class PropertiesImplementation {
} else {
absVal = createAbstractPropertyValue(Value);
}
return { configurable: true, enumerable: true, value: absVal, writable: true };
return new PropertyDescriptor({ configurable: true, enumerable: true, value: absVal, writable: true });
} else {
invariant(P instanceof SymbolValue);
// Simple objects don't have symbol properties
@ -1490,18 +1551,17 @@ export class PropertiesImplementation {
}
// 3. Let D be a newly created Property Descriptor with no fields.
let D = {};
let D = new PropertyDescriptor({});
// 4. Let X be O's own property whose key is P.
let X = existingBinding.descriptor;
invariant(X !== undefined);
if (X.joinCondition !== undefined) {
D.joinCondition = X.joinCondition;
D.descriptor1 = X.descriptor1;
D.descriptor2 = X.descriptor2;
return D;
if (X instanceof AbstractJoinedDescriptor) {
return new AbstractJoinedDescriptor(X.joinCondition, X.descriptor1, X.descriptor2);
}
invariant(X instanceof PropertyDescriptor);
// 5. If X is a data property, then
if (IsDataDescriptor(realm, X)) {
let value = X.value;
@ -1536,12 +1596,17 @@ export class PropertiesImplementation {
realm.markPropertyAsChecked(O, P);
realmGenerator.emitFullInvariant(O, P, value);
}
InternalSetProperty(realm, O, P, {
value: value,
writable: "writable" in X ? X.writable : false,
enumerable: "enumerable" in X ? X.enumerable : false,
configurable: "configurable" in X ? X.configurable : false,
});
InternalSetProperty(
realm,
O,
P,
new PropertyDescriptor({
value: value,
writable: X.writable !== undefined ? X.writable : false,
enumerable: X.enumerable !== undefined ? X.enumerable : false,
configurable: X.configurable !== undefined ? X.configurable : false,
})
);
}
} else if (realm.invariantLevel >= 1 && value instanceof Value && !(value instanceof AbstractValue)) {
let realmGenerator = realm.generator;
@ -1678,8 +1743,8 @@ export class PropertiesImplementation {
// Omit non-enumerable properties.
let desc = obj.$GetOwnProperty(key);
if (desc && !desc.enumerable) {
this.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc && !desc.throwIfNotConcrete(realm).enumerable) {
this.ThrowIfMightHaveBeenDeleted(desc);
index += 1;
visited.add(key.value);
continue;
@ -1699,10 +1764,20 @@ export class PropertiesImplementation {
return iterator;
}
ThrowIfMightHaveBeenDeleted(
value: void | Value | Array<Value> | Array<{ $Key: void | Value, $Value: void | Value }>
): void {
if (!(value instanceof Value)) return;
ThrowIfMightHaveBeenDeleted(desc: Descriptor): void {
if (desc instanceof AbstractJoinedDescriptor) {
if (desc.descriptor1) {
this.ThrowIfMightHaveBeenDeleted(desc.descriptor1);
}
if (desc.descriptor2) {
this.ThrowIfMightHaveBeenDeleted(desc.descriptor2);
}
}
invariant(desc instanceof PropertyDescriptor, "internal slots should never assert using this");
let value = desc.value;
if (value === undefined) {
return;
}
if (!value.mightHaveBeenDeleted()) return;
invariant(value instanceof AbstractValue); // real empty values should never get here
let v = value.$Realm.simplifyAndRefineAbstractValue(value);
@ -1742,7 +1817,12 @@ export class PropertiesImplementation {
methodDef.$Closure.$HasComputedName = !!MethodDefinition.computed;
// 4. Let desc be the Property Descriptor{[[Value]]: methodDef.[[closure]], [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
let desc: Descriptor = { value: methodDef.$Closure, writable: true, enumerable: enumerable, configurable: true };
let desc: Descriptor = new PropertyDescriptor({
value: methodDef.$Closure,
writable: true,
enumerable: enumerable,
configurable: true,
});
// 5. Return DefinePropertyOrThrow(object, methodDef.[[key]], desc).
return this.DefinePropertyOrThrow(realm, object, methodDef.$Key, desc);
@ -1784,7 +1864,12 @@ export class PropertiesImplementation {
Functions.SetFunctionName(realm, closure, propKey);
// 10. Let desc be the Property Descriptor{[[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true}.
let desc: Descriptor = { value: closure, writable: true, enumerable: enumerable, configurable: true };
let desc: Descriptor = new PropertyDescriptor({
value: closure,
writable: true,
enumerable: enumerable,
configurable: true,
});
// 11. Return DefinePropertyOrThrow(object, propKey, desc).
return this.DefinePropertyOrThrow(realm, object, propKey, desc);
@ -1823,11 +1908,11 @@ export class PropertiesImplementation {
closure.$HasComputedName = !!MethodDefinition.computed;
// 9. Let desc be the PropertyDescriptor{[[Get]]: closure, [[Enumerable]]: enumerable, [[Configurable]]: true}.
let desc = {
let desc = new PropertyDescriptor({
get: closure,
enumerable: true,
configurable: true,
};
});
// 10. Return ? DefinePropertyOrThrow(object, propKey, desc).
return this.DefinePropertyOrThrow(realm, object, propKey, desc);
@ -1864,11 +1949,11 @@ export class PropertiesImplementation {
closure.$HasComputedName = !!MethodDefinition.computed;
// 8. Let desc be the PropertyDescriptor{[[Set]]: closure, [[Enumerable]]: enumerable, [[Configurable]]: true}.
let desc = {
let desc = new PropertyDescriptor({
set: closure,
enumerable: true,
configurable: true,
};
});
// 9. Return ? DefinePropertyOrThrow(object, propKey, desc).
return this.DefinePropertyOrThrow(realm, object, propKey, desc);
@ -1894,7 +1979,7 @@ export class PropertiesImplementation {
keyArray = keyArray.filter(x => {
let pb = O.properties.get(x);
if (!pb || pb.descriptor === undefined) return false;
let pv = pb.descriptor.value;
let pv = pb.descriptor.throwIfNotConcrete(realm).value;
if (pv === undefined) return true;
invariant(pv instanceof Value);
if (!pv.mightHaveBeenDeleted()) return true;

View File

@ -17,6 +17,7 @@ import { IsCallable } from "./is.js";
import { Call } from "./call.js";
import { HasCompatibleType, HasSomeCompatibleType } from "./has.js";
import { Create, Properties, To } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
// ECMA262 21.2.3.2.3
export function RegExpCreate(realm: Realm, P: ?Value, F: ?Value): ObjectValue {
@ -39,11 +40,16 @@ export function RegExpAlloc(realm: Realm, newTarget: ObjectValue): ObjectValue {
// 2. Perform ! DefinePropertyOrThrow(obj, "lastIndex", PropertyDescriptor {[[Writable]]: true,
// [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, obj, "lastIndex", {
writable: true,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(
realm,
obj,
"lastIndex",
new PropertyDescriptor({
writable: true,
enumerable: false,
configurable: false,
})
);
// 3. Return obj.
return obj;

View File

@ -36,6 +36,7 @@ import {
} from "../values/index.js";
import invariant from "../invariant.js";
import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
type ElementConvType = {
Int8: (Realm, numberOrValue) => number,
@ -278,7 +279,7 @@ export class ToImplementation {
}
// 2. Let desc be a new Property Descriptor that initially has no fields.
let desc: Descriptor = {};
let desc = new PropertyDescriptor({});
// 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
let hasEnumerable = HasProperty(realm, Obj, "enumerable");
@ -367,7 +368,7 @@ export class ToImplementation {
// 15. If either desc.[[Get]] or desc.[[Set]] is present, then
if (desc.get || desc.set) {
// a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception.
if ("value" in desc || "writable" in desc) {
if (desc.value !== undefined || desc.writable !== undefined) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
}

View File

@ -14,14 +14,16 @@ import { FatalError } from "../errors.js";
import type { Bindings, BindingEntry, EvaluationResult, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
import { Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js";
import { cloneDescriptor, equalDescriptors, PropertyDescriptor } from "../descriptors.js";
import { AbruptCompletion, JoinedNormalAndAbruptCompletions, SimpleNormalCompletion } from "../completions.js";
import { Reference } from "../environment.js";
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "./index.js";
import { IsDataDescriptor, StrictEqualityComparison } from "./index.js";
import { Generator, createOperationDescriptor } from "../utils/generator.js";
import { AbstractValue, ArrayValue, EmptyValue, Value, StringValue } from "../values/index.js";
import invariant from "../invariant.js";
import { InternalSlotDescriptor } from "../descriptors.js";
export class WidenImplementation {
_widenArrays(
@ -212,17 +214,18 @@ export class WidenImplementation {
if (d1 === undefined && d2 === undefined) return undefined;
// If the PropertyBinding object has been freshly allocated do not widen (that happens in AbstractObjectValue)
if (d1 === undefined) {
invariant(d2 !== undefined);
if (c2.has(b.object)) return d2; // no widen
if (b.descriptor !== undefined && m1.has(b)) {
// property was present in (n-1)th iteration and deleted in nth iteration
d1 = cloneDescriptor(b.descriptor);
d1 = cloneDescriptor(b.descriptor.throwIfNotConcrete(realm));
invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty;
} else {
// no write to property in nth iteration, use the value from the (n-1)th iteration
d1 = b.descriptor;
if (d1 === undefined) {
d1 = cloneDescriptor(d2);
d1 = cloneDescriptor(d2.throwIfNotConcrete(realm));
invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty;
}
@ -232,7 +235,7 @@ export class WidenImplementation {
if (c1.has(b.object)) return d1; // no widen
if (m2.has(b)) {
// property was present in nth iteration and deleted in (n+1)th iteration
d2 = cloneDescriptor(d1);
d2 = cloneDescriptor(d1.throwIfNotConcrete(realm));
invariant(d2 !== undefined);
d2.value = realm.intrinsics.empty;
} else {
@ -277,8 +280,7 @@ export class WidenImplementation {
// before the loop commences, otherwise the memberExpression will result in an undefined value.
let generator = realm.generator;
invariant(generator !== undefined);
let initVal = (b.descriptor && b.descriptor.value) || realm.intrinsics.empty;
if (!(initVal instanceof Value)) throw new FatalError("todo: handle internal properties");
let initVal = (b.descriptor && b.descriptor.throwIfNotConcrete(realm).value) || realm.intrinsics.empty;
if (!(initVal instanceof EmptyValue)) {
if (key === "length" && b.object instanceof ArrayValue) {
// do nothing, the array length will already be initialized
@ -311,7 +313,8 @@ export class WidenImplementation {
return this.widenMaps(m1, m2, widen);
}
widenDescriptors(realm: Realm, d1: void | Descriptor, d2: Descriptor): void | Descriptor {
widenDescriptors(realm: Realm, d1: void | Descriptor, d2: Descriptor): void | PropertyDescriptor {
d2 = d2.throwIfNotConcrete(realm);
if (d1 === undefined) {
// d2 is a property written to only in the (n+1)th iteration
if (!IsDataDescriptor(realm, d2)) return d2; // accessor properties need not be widened.
@ -319,9 +322,12 @@ export class WidenImplementation {
invariant(dc !== undefined);
let d2value = dc.value;
invariant(d2value !== undefined); // because IsDataDescriptor is true for d2/dc
dc.value = this.widenValues(realm, d2value, d2value);
let dcValue = this.widenValues(realm, d2value, d2value);
invariant(dcValue instanceof Value);
dc.value = dcValue;
return dc;
} else {
d1 = d1.throwIfNotConcrete(realm);
if (equalDescriptors(d1, d2)) {
if (!IsDataDescriptor(realm, d1)) return d1; // identical accessor properties need not be widened.
// equalDescriptors plus IsDataDescriptor guarantee that both have value props and if you have a value prop is value is defined.
@ -331,7 +337,9 @@ export class WidenImplementation {
invariant(d1value !== undefined);
let d2value = d2.value;
invariant(d2value !== undefined);
dc.value = this.widenValues(realm, d1value, d2value);
let dcValue = this.widenValues(realm, d1value, d2value);
invariant(dcValue instanceof Value);
dc.value = dcValue;
return dc;
}
//todo: #1174 if we get here, the loop body contains a call to create a property and different iterations
@ -397,7 +405,23 @@ export class WidenImplementation {
c2: CreatedObjects
): boolean {
let containsPropertyBinding = (d1: void | Descriptor, d2: void | Descriptor) => {
let [v1, v2] = [d1 && d1.value, d2 && d2.value];
let v1, v2;
if (d1 instanceof InternalSlotDescriptor || d2 instanceof InternalSlotDescriptor) {
if (d1 !== undefined) {
invariant(d1 instanceof InternalSlotDescriptor);
v1 = d1.value;
}
if (d2 !== undefined) {
invariant(d2 instanceof InternalSlotDescriptor);
v2 = d2.value;
}
}
if (d1 instanceof PropertyDescriptor) {
v1 = d1.value;
}
if (d2 instanceof PropertyDescriptor) {
v2 = d2.value;
}
if (v1 === undefined) {
return v2 === undefined;
}

View File

@ -76,8 +76,4 @@ export type SerializerOptions = {
heapGraphFormat?: "DotLanguage" | "VISJS",
};
export type PartialEvaluatorOptions = {
sourceMaps?: boolean,
};
export const defaultOptions = {};

View File

@ -21,6 +21,7 @@ import {
InvariantModeValues,
} from "./options.js";
import { type SerializedResult } from "./serializer/types.js";
import { TextPrinter } from "./utils/TextPrinter.js";
import { prepackStdin, prepackFileSync } from "./prepack-node.js";
import type { BabelNodeSourceLocation } from "@babel/types";
import fs from "fs";
@ -67,6 +68,7 @@ function run(
--logStatistics Log statistics to console
--statsFile The name of the output file where statistics will be written to.
--heapGraphFilePath The name of the output file where heap graph will be written to.
--dumpIRFilePath The name of the output file where the intermediate representation will be written to.
--inlineExpressions When generating code, tells prepack to avoid naming expressions when they are only used once,
and instead inline them where they are used.
--invariantLevel 0: no invariants (default); 1: checks for abstract values; 2: checks for accessed built-ins; 3: internal consistency
@ -94,6 +96,7 @@ function run(
let debugIdentifiers: void | Array<string>;
let lazyObjectsRuntime: string;
let heapGraphFilePath: void | string;
let dumpIRFilePath: void | string;
let debugInFilePath: string;
let debugOutFilePath: string;
let reactOutput: ReactOutputTypes = "create-element";
@ -230,6 +233,10 @@ function run(
heapGraphFilePath = args.shift();
// do not include this in reproArguments needed by --repro[OnFatalError/Unconditionally], as path is likely not portable between environments
break;
case "dumpIRFilePath":
dumpIRFilePath = args.shift();
// do not include this in reproArguments needed by --repro[OnFatalError/Unconditionally], as path is likely not portable between environments
break;
case "reactOutput":
arg = args.shift();
if (!ReactOutputValues.includes(arg)) {
@ -306,6 +313,7 @@ function run(
"--check [start[, number]]",
"--lazyObjectsRuntime lazyObjectsRuntimeName",
"--heapGraphFilePath heapGraphFilePath",
"--dumpIRFilePath dumpIRFilePath",
"--reactOutput " + ReactOutputValues.join(" | "),
"--repro reprofile.zip",
"--cpuprofile name.cpuprofile",
@ -355,6 +363,16 @@ function run(
flags
);
if (heapGraphFilePath !== undefined) resolvedOptions.heapGraphFormat = "DotLanguage";
if (dumpIRFilePath !== undefined) {
resolvedOptions.onExecute = (realm, optimizedFunctions) => {
let text = "";
new TextPrinter(line => {
text += line + "\n";
}).print(realm, optimizedFunctions);
invariant(dumpIRFilePath !== undefined);
fs.writeFileSync(dumpIRFilePath, text);
};
}
if (lazyObjectsRuntime !== undefined && (resolvedOptions.delayInitializations || resolvedOptions.inlineExpressions)) {
console.error("lazy objects feature is incompatible with delayInitializations and inlineExpressions options");
process.exit(1);

View File

@ -11,7 +11,9 @@
import type { ErrorHandler } from "./errors.js";
import type { SerializerOptions, RealmOptions, Compatibility, ReactOutputTypes, InvariantModeTypes } from "./options";
import { Realm } from "./realm.js";
import { type Realm } from "./realm.js";
import { type Generator } from "./utils/generator.js";
import { type FunctionValue } from "./values/index.js";
import type { DebuggerConfigArguments, DebugReproArguments } from "./types";
import type { BabelNodeFile } from "@babel/types";
@ -58,6 +60,7 @@ export type PrepackOptions = {|
debuggerConfigArgs?: DebuggerConfigArguments,
debugReproArgs?: DebugReproArguments,
onParse?: BabelNodeFile => void,
onExecute?: (Realm, Map<FunctionValue, Generator>) => void,
arrayNestedOptimizedFunctionsEnabled?: boolean,
|};

View File

@ -62,7 +62,7 @@ export function prepackSources(
return { code: "", map: undefined };
} else {
let serializer = new Serializer(realm, getSerializerOptions(options));
let serialized = serializer.init(sourceFileCollection, options.sourceMaps, options.onParse);
let serialized = serializer.init(sourceFileCollection, options.sourceMaps, options.onParse, options.onExecute);
//Turn off the debugger if there is one
if (realm.debuggerInstance) {

View File

@ -38,6 +38,7 @@ import type { ClassComponentMetadata, ReactComponentTreeConfig } from "../types.
import type { ReactEvaluatedNode } from "../serializer/types.js";
import { FatalError } from "../errors.js";
import { type ComponentModel, ShapeInformation } from "../utils/ShapeInformation.js";
import { PropertyDescriptor } from "../descriptors.js";
const lifecycleMethods = new Set([
"componentWillUnmount",
@ -231,9 +232,12 @@ export function createClassInstanceForFirstRenderOnly(
newState.makeFinal();
for (let [key, binding] of stateToUpdate.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
let value = getProperty(realm, stateToUpdate, key);
hardModifyReactObjectPropertyBinding(realm, newState, key, value);
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable) {
let value = getProperty(realm, stateToUpdate, key);
hardModifyReactObjectPropertyBinding(realm, newState, key, value);
}
}
}

View File

@ -35,6 +35,7 @@ import {
import { computeBinary } from "../evaluators/BinaryExpression.js";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
function createPropsObject(
realm: Realm,
@ -110,8 +111,11 @@ function createPropsObject(
const applyProperties = () => {
if (config instanceof ObjectValue) {
for (let [propKey, binding] of config.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
setProp(propKey, Get(realm, config, propKey));
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable) {
setProp(propKey, Get(realm, config, propKey));
}
}
}
}
@ -138,19 +142,27 @@ function createPropsObject(
// first see if we can apply all the defaultProps without needing the helper
if (defaultProps instanceof ObjectValue && !defaultProps.isPartialObject()) {
for (let [propName, binding] of defaultProps.properties) {
if (binding.descriptor !== undefined && binding.descriptor.value !== realm.intrinsics.undefined) {
// see if we have this on our props object
let propBinding = props.properties.get(propName);
// if the binding exists and value is abstract, it might be undefined
// so in that case we need the helper, otherwise we can continue
if (
propBinding !== undefined &&
!(propBinding.descriptor && propBinding.descriptor.value instanceof AbstractValue)
) {
defaultPropsEvaluated++;
// if the value we have is undefined, we can apply the defaultProp
if (propBinding.descriptor && propBinding.descriptor.value === realm.intrinsics.undefined) {
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName));
if (binding.descriptor !== undefined) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.value !== realm.intrinsics.undefined) {
// see if we have this on our props object
let propBinding = props.properties.get(propName);
// if the binding exists and value is abstract, it might be undefined
// so in that case we need the helper, otherwise we can continue
if (
propBinding !== undefined &&
!(
propBinding.descriptor instanceof PropertyDescriptor &&
propBinding.descriptor.value instanceof AbstractValue
)
) {
defaultPropsEvaluated++;
// if the value we have is undefined, we can apply the defaultProp
if (propBinding.descriptor) {
invariant(propBinding.descriptor instanceof PropertyDescriptor);
if (propBinding.descriptor.value === realm.intrinsics.undefined)
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName));
}
}
}
}
@ -168,9 +180,12 @@ function createPropsObject(
// as the helper function applies defaultProps on values that are undefined or do not
// exist
for (let [propName, binding] of props.properties) {
if (binding.descriptor !== undefined && binding.descriptor.value === realm.intrinsics.undefined) {
invariant(defaultProps instanceof AbstractObjectValue || defaultProps instanceof ObjectValue);
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName));
if (binding.descriptor !== undefined) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.value === realm.intrinsics.undefined) {
invariant(defaultProps instanceof AbstractObjectValue || defaultProps instanceof ObjectValue);
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName));
}
}
}
// if we have children and they are abstract, they might be undefined at runtime
@ -218,8 +233,9 @@ function createPropsObject(
invariant(false, "TODO: we need to eventually support this");
} else if (defaultProps instanceof ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
if (Get(realm, props, propKey) === realm.intrinsics.undefined) {
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable && Get(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, Get(realm, defaultProps, propKey));
}
}
@ -348,8 +364,9 @@ export function cloneReactElement(
if (defaultProps instanceof ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
if (Get(realm, props, propKey) === realm.intrinsics.undefined) {
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable && Get(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, Get(realm, defaultProps, propKey));
}
}

View File

@ -77,6 +77,7 @@ import { Logger } from "../utils/logger.js";
import type { ClassComponentMetadata, ReactComponentTreeConfig, ReactHint } from "../types.js";
import { handleReportedSideEffect } from "../serializer/utils.js";
import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
type ComponentResolutionStrategy =
| "NORMAL"
@ -127,6 +128,7 @@ function setContextCurrentValue(contextObject: ObjectValue | AbstractObjectValue
let binding = contextObject.properties.get("currentValue");
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
binding.descriptor.value = value;
} else {
invariant(false, "setContextCurrentValue failed to set the currentValue");
@ -1477,15 +1479,18 @@ export class Reconciler {
this._findReactComponentTrees(props, evaluatedNode, treatFunctionsAs, componentType, context, branchStatus);
} else {
for (let [propName, binding] of value.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
this._findReactComponentTrees(
getProperty(this.realm, value, propName),
evaluatedNode,
treatFunctionsAs,
componentType,
context,
branchStatus
);
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable) {
this._findReactComponentTrees(
getProperty(this.realm, value, propName),
evaluatedNode,
treatFunctionsAs,
componentType,
context,
branchStatus
);
}
}
}
}

View File

@ -31,7 +31,7 @@ import {
} from "../values/index.js";
import { TemporalObjectAssignEntry } from "../utils/generator.js";
import type { Descriptor, ReactComponentTreeConfig, ReactHint, PropertyBinding } from "../types.js";
import { Get, cloneDescriptor } from "../methods/index.js";
import { Get, IsDataDescriptor } from "../methods/index.js";
import { computeBinary } from "../evaluators/BinaryExpression.js";
import type { AdditionalFunctionEffects, ReactEvaluatedNode } from "../serializer/types.js";
import invariant from "../invariant.js";
@ -40,6 +40,7 @@ import traverse from "@babel/traverse";
import * as t from "@babel/types";
import type { BabelNodeStatement } from "@babel/types";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { cloneDescriptor, PropertyDescriptor } from "../descriptors.js";
export type ReactSymbolTypes =
| "react.element"
@ -109,6 +110,7 @@ export function getReactSymbol(symbolKey: ReactSymbolTypes, realm: Realm): Symbo
let SymbolForDescriptor = SymbolFor.descriptor;
if (SymbolForDescriptor !== undefined) {
invariant(SymbolForDescriptor instanceof PropertyDescriptor);
let SymbolForValue = SymbolForDescriptor.value;
if (SymbolForValue instanceof ObjectValue && typeof SymbolForValue.$Call === "function") {
reactSymbol = SymbolForValue.$Call(realm.intrinsics.Symbol, [new StringValue(realm, symbolKey)]);
@ -243,6 +245,7 @@ export function forEachArrayValue(
let elementProperty = array.properties.get("" + i);
let elementPropertyDescriptor = elementProperty && elementProperty.descriptor;
if (elementPropertyDescriptor) {
invariant(elementPropertyDescriptor instanceof PropertyDescriptor);
let elementValue = elementPropertyDescriptor.value;
if (elementValue instanceof Value) {
mapFunc(elementValue, i);
@ -266,6 +269,7 @@ export function mapArrayValue(
let elementProperty = array.properties.get("" + i);
let elementPropertyDescriptor = elementProperty && elementProperty.descriptor;
if (elementPropertyDescriptor) {
invariant(elementPropertyDescriptor instanceof PropertyDescriptor);
let elementValue = elementPropertyDescriptor.value;
if (elementValue instanceof Value) {
let newElement = mapFunc(elementValue, elementPropertyDescriptor);
@ -294,7 +298,7 @@ export function convertSimpleClassComponentToFunctionalComponent(
): void {
let prototype = complexComponentType.properties.get("prototype");
invariant(prototype);
invariant(prototype.descriptor);
invariant(prototype.descriptor instanceof PropertyDescriptor);
prototype.descriptor.configurable = true;
Properties.DeletePropertyOrThrow(realm, complexComponentType, "prototype");
@ -349,7 +353,10 @@ function createBinding(descriptor: void | Descriptor, key: string | SymbolValue,
function cloneProperties(realm: Realm, properties: Map<string, any>, object: ObjectValue): Map<string, any> {
let newProperties = new Map();
for (let [propertyName, { descriptor }] of properties) {
newProperties.set(propertyName, createBinding(cloneDescriptor(descriptor), propertyName, object));
newProperties.set(
propertyName,
createBinding(cloneDescriptor(descriptor.throwIfNotConcrete(realm)), propertyName, object)
);
}
return newProperties;
}
@ -357,7 +364,7 @@ function cloneProperties(realm: Realm, properties: Map<string, any>, object: Obj
function cloneSymbols(realm: Realm, symbols: Map<SymbolValue, any>, object: ObjectValue): Map<SymbolValue, any> {
let newSymbols = new Map();
for (let [symbol, { descriptor }] of symbols) {
newSymbols.set(symbol, createBinding(cloneDescriptor(descriptor), symbol, object));
newSymbols.set(symbol, createBinding(cloneDescriptor(descriptor.throwIfNotConcrete(realm)), symbol, object));
}
return newSymbols;
}
@ -482,7 +489,7 @@ export function convertFunctionalComponentToComplexClassComponent(
export function normalizeFunctionalComponentParamaters(func: ECMAScriptSourceFunctionValue): void {
// fix the length as we may change the arguments
let lengthProperty = GetDescriptorForProperty(func, "length");
invariant(lengthProperty);
invariant(lengthProperty instanceof PropertyDescriptor);
lengthProperty.writable = false;
lengthProperty.enumerable = false;
lengthProperty.configurable = true;
@ -637,6 +644,7 @@ export function getProperty(
if (!descriptor) {
return realm.intrinsics.undefined;
}
invariant(descriptor instanceof PropertyDescriptor);
let value = descriptor.value;
if (value === undefined) {
AbstractValue.reportIntrospectionError(object, `react/utils/getProperty unsupported getter/setter property`);
@ -912,11 +920,14 @@ export function cloneProps(realm: Realm, props: ObjectValue, newChildren?: Value
let clonedProps = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
for (let [propName, binding] of props.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) {
if (newChildren !== undefined && propName === "children") {
Properties.Set(realm, clonedProps, propName, newChildren, true);
} else {
Properties.Set(realm, clonedProps, propName, getProperty(realm, props, propName), true);
if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable) {
if (newChildren !== undefined && propName === "children") {
Properties.Set(realm, clonedProps, propName, newChildren, true);
} else {
Properties.Set(realm, clonedProps, propName, getProperty(realm, props, propName), true);
}
}
}
}
@ -999,20 +1010,19 @@ export function hardModifyReactObjectPropertyBinding(
if (binding === undefined) {
binding = {
object,
descriptor: {
descriptor: new PropertyDescriptor({
configurable: true,
enumerable: true,
value: undefined,
writable: true,
},
}),
key: propName,
};
}
let descriptor = binding.descriptor;
invariant(descriptor !== undefined);
let newDescriptor = Object.assign({}, descriptor, {
value,
});
invariant(descriptor instanceof PropertyDescriptor && IsDataDescriptor(realm, descriptor));
let newDescriptor = new PropertyDescriptor(descriptor);
newDescriptor.value = value;
let newBinding = Object.assign({}, binding, {
descriptor: newDescriptor,
});

View File

@ -54,7 +54,7 @@ import {
DeclarativeEnvironmentRecord,
} from "./environment.js";
import type { Binding } from "./environment.js";
import { cloneDescriptor, Construct } from "./methods/index.js";
import { Construct } from "./methods/index.js";
import {
AbruptCompletion,
Completion,
@ -81,6 +81,12 @@ import {
Widen,
} from "./singletons.js";
import type { ReactSymbolTypes } from "./react/utils.js";
import {
cloneDescriptor,
AbstractJoinedDescriptor,
InternalSlotDescriptor,
PropertyDescriptor,
} from "./descriptors.js";
import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal } from "@babel/types";
export type BindingEntry = { hasLeaked: boolean, value: void | Value };
export type Bindings = Map<Binding, BindingEntry>;
@ -641,7 +647,7 @@ export class Realm {
invariant(globalObject instanceof ObjectValue);
let binding = globalObject.properties.get("__checkedBindings");
invariant(binding !== undefined);
let checkedBindingsObject = binding.descriptor && binding.descriptor.value;
let checkedBindingsObject = binding.descriptor && binding.descriptor.throwIfNotConcrete(this).value;
invariant(checkedBindingsObject instanceof ObjectValue);
return checkedBindingsObject;
}
@ -662,7 +668,7 @@ export class Realm {
let id = `__propertyHasBeenChecked__${objectId}:${P}`;
let binding = this._getCheckedBindings().properties.get(id);
if (binding === undefined) return false;
let value = binding.descriptor && binding.descriptor.value;
let value = binding.descriptor && binding.descriptor.throwIfNotConcrete(this).value;
return value instanceof Value && !value.mightNotBeTrue();
}
@ -1080,7 +1086,7 @@ export class Realm {
if (newlyCreatedObjects.has(key.object) || key.object.refuseSerialization) {
return;
}
let value = val && val.value;
let value = val && val.throwIfNotConcrete(this).value;
if (value instanceof AbstractValue) {
invariant(value.operationDescriptor !== undefined);
let tval = gen.deriveAbstract(
@ -1102,7 +1108,7 @@ export class Realm {
let path = key.pathNode;
let tval = tvalFor.get(key);
invariant(val !== undefined);
let value = val.value;
let value = val.throwIfNotConcrete(this).value;
invariant(value instanceof Value);
let keyKey = key.key;
if (typeof keyKey === "string") {
@ -1516,7 +1522,20 @@ export class Realm {
}
this.callReportPropertyAccess(binding);
if (this.modifiedProperties !== undefined && !this.modifiedProperties.has(binding)) {
this.modifiedProperties.set(binding, cloneDescriptor(binding.descriptor));
let clone;
let desc = binding.descriptor;
if (desc === undefined) {
clone = undefined;
} else if (desc instanceof AbstractJoinedDescriptor) {
clone = new AbstractJoinedDescriptor(desc.joinCondition, desc.descriptor1, desc.descriptor2);
} else if (desc instanceof PropertyDescriptor) {
clone = cloneDescriptor(desc);
} else if (desc instanceof InternalSlotDescriptor) {
clone = new InternalSlotDescriptor(desc.value);
} else {
invariant(false, "unknown descriptor");
}
this.modifiedProperties.set(binding, clone);
}
}
@ -1604,8 +1623,9 @@ export class Realm {
for (let [key, binding] of template.properties) {
if (binding === undefined || binding.descriptor === undefined) continue; // deleted
invariant(binding.descriptor !== undefined);
let value = binding.descriptor.value;
Properties.ThrowIfMightHaveBeenDeleted(value);
let desc = binding.descriptor.throwIfNotConcrete(this);
let value = desc.value;
Properties.ThrowIfMightHaveBeenDeleted(desc);
if (value === undefined) {
AbstractValue.reportIntrospectionError(abstractValue, key);
throw new FatalError();

View File

@ -33,7 +33,7 @@ function describeValue(realm: Realm, v: Value): string {
if (v instanceof NumberValue || v instanceof BooleanValue) return v.value.toString();
if (v instanceof UndefinedValue) return "undefined";
if (v instanceof NullValue) return "null";
if (v instanceof StringValue) return `"${v.value}"`; // TODO: proper escaping
if (v instanceof StringValue) return JSON.stringify(v.value);
if (v instanceof FunctionValue) return To.ToStringPartial(realm, Get(realm, v, "name")) || "(anonymous function)";
if (v instanceof ObjectValue) return "(some object)";
if (v instanceof AbstractValue) return "(some abstract value)";

View File

@ -86,6 +86,7 @@ import { type Replacement, getReplacement } from "./ResidualFunctionInstantiator
import { describeValue } from "../utils.js";
import { getAsPropertyNameExpression } from "../utils/babelhelpers.js";
import { ResidualOperationSerializer } from "./ResidualOperationSerializer.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
function commentStatement(text: string) {
let s = t.emptyStatement();
@ -317,7 +318,8 @@ export class ResidualHeapSerializer {
// TODO #2259: Make deduplication in the face of leaking work for custom accessors
let isCertainlyLeaked = !obj.mightNotBeLeakedObject();
let shouldDropAsAssignedProp = (descriptor: Descriptor | void) =>
isCertainlyLeaked && (descriptor !== undefined && (descriptor.get === undefined && descriptor.set === undefined));
isCertainlyLeaked &&
(descriptor instanceof PropertyDescriptor && (descriptor.get === undefined && descriptor.set === undefined));
// inject properties
for (let [key, propertyBinding] of properties) {
@ -348,14 +350,11 @@ export class ResidualHeapSerializer {
if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor;
if (desc !== undefined) {
let val = desc.value;
invariant(val instanceof AbstractValue);
let semaphore = this._acquireOneObjectSemaphore(obj);
this.emitter.emitNowOrAfterWaitingForDependencies(
this._getNestedValuesFromAbstract(val, [obj]),
this._getNestedValuesFromAbstractDescriptor(desc, [obj]),
() => {
invariant(val instanceof AbstractValue);
this._emitPropertiesWithComputedNames(obj, val);
this._emitPropertiesWithComputedNamesDescriptor(obj, desc);
if (semaphore !== undefined) semaphore.releaseOne();
},
this.emitter.getBody()
@ -444,6 +443,22 @@ export class ResidualHeapSerializer {
}
}
_getNestedValuesFromAbstractDescriptor(desc: void | Descriptor, values: Array<Value>): Array<Value> {
if (desc === undefined) return values;
if (desc instanceof PropertyDescriptor) {
let val = desc.value;
invariant(val instanceof AbstractValue);
return this._getNestedValuesFromAbstract(val, values);
} else if (desc instanceof AbstractJoinedDescriptor) {
values.push(desc.joinCondition);
this._getNestedValuesFromAbstractDescriptor(desc.descriptor1, values);
this._getNestedValuesFromAbstractDescriptor(desc.descriptor2, values);
return values;
} else {
invariant(false, "unknown descriptor");
}
}
_getNestedValuesFromAbstract(absVal: AbstractValue, values: Array<Value>): Array<Value> {
if (absVal.kind === "widened property") return values;
if (absVal.kind === "template for prototype member expression") return values;
@ -477,6 +492,60 @@ export class ResidualHeapSerializer {
return values;
}
_emitPropertiesWithComputedNamesDescriptor(obj: ObjectValue, desc: void | Descriptor): void {
if (desc === undefined) return;
if (desc instanceof PropertyDescriptor) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this._emitPropertiesWithComputedNames(obj, val);
} else if (desc instanceof AbstractJoinedDescriptor) {
let serializedCond = this.serializeValue(desc.joinCondition);
let valuesToProcess = new Set();
let consequentStatement;
let alternateStatement;
if (desc.descriptor1) {
let oldBody = this.emitter.beginEmitting(
"consequent",
{
type: "ConditionalAssignmentBranch",
parentBody: undefined,
entries: [],
done: false,
},
/*isChild*/ true
);
this._emitPropertiesWithComputedNamesDescriptor(obj, desc.descriptor1);
let consequentBody = this.emitter.endEmitting("consequent", oldBody, valuesToProcess, /*isChild*/ true);
consequentStatement = t.blockStatement(consequentBody.entries);
}
if (desc.descriptor2) {
let oldBody = this.emitter.beginEmitting(
"alternate",
{
type: "ConditionalAssignmentBranch",
parentBody: undefined,
entries: [],
done: false,
},
/*isChild*/ true
);
this._emitPropertiesWithComputedNamesDescriptor(obj, desc.descriptor2);
let alternateBody = this.emitter.endEmitting("alternate", oldBody, valuesToProcess, /*isChild*/ true);
alternateStatement = t.blockStatement(alternateBody.entries);
}
if (consequentStatement) {
this.emitter.emit(t.ifStatement(serializedCond, consequentStatement, alternateStatement));
} else if (alternateStatement) {
this.emitter.emit(t.ifStatement(t.unaryExpression("!", serializedCond), alternateStatement));
}
this.emitter.processValues(valuesToProcess);
} else {
invariant(false, "unknown descriptor");
}
}
_emitPropertiesWithComputedNames(obj: ObjectValue, absVal: AbstractValue): void {
if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return;
@ -577,7 +646,7 @@ export class ResidualHeapSerializer {
key: string | SymbolValue | AbstractValue,
desc: Descriptor
): BabelNodeStatement {
if (desc.joinCondition) {
if (desc instanceof AbstractJoinedDescriptor) {
let cond = this.serializeValue(desc.joinCondition);
invariant(cond !== undefined);
let trueBody;
@ -603,6 +672,7 @@ export class ResidualHeapSerializer {
if (falseBody) return t.ifStatement(t.unaryExpression("!", cond), falseBody);
invariant(false);
}
invariant(desc instanceof PropertyDescriptor);
if (locationFunction !== undefined && this._canEmbedProperty(val, key, desc)) {
let descValue = desc.value;
invariant(descValue instanceof Value);
@ -630,8 +700,8 @@ export class ResidualHeapSerializer {
let descriptorsKey = [];
for (let boolKey of boolKeys) {
if (boolKey in desc) {
let b = desc[boolKey];
if ((desc: any)[boolKey] !== undefined) {
let b: boolean = (desc: any)[boolKey];
invariant(b !== undefined);
descProps.push(t.objectProperty(t.identifier(boolKey), t.booleanLiteral(b)));
descriptorsKey.push(`${boolKey}:${b.toString()}`);
@ -650,8 +720,8 @@ export class ResidualHeapSerializer {
invariant(descriptorId !== undefined);
for (let descKey of valKeys) {
if (descKey in desc) {
let descValue = desc[descKey];
if ((desc: any)[descKey] !== undefined) {
let descValue: Value = (desc: any)[descKey];
invariant(descValue instanceof Value);
if (descValue instanceof UndefinedValue) {
this.serializeValue(descValue);
@ -1172,13 +1242,24 @@ export class ResidualHeapSerializer {
}
}
_getDescriptorValues(desc: Descriptor): Array<Value> {
if (desc.joinCondition !== undefined) return [desc.joinCondition];
invariant(desc.value === undefined || desc.value instanceof Value);
if (desc.value !== undefined) return [desc.value];
invariant(desc.get !== undefined);
invariant(desc.set !== undefined);
return [desc.get, desc.set];
_getDescriptorValues(desc: void | Descriptor): Array<Value> {
if (desc === undefined) {
return [];
} else if (desc instanceof PropertyDescriptor) {
invariant(desc.value === undefined || desc.value instanceof Value);
if (desc.value !== undefined) return [desc.value];
invariant(desc.get !== undefined);
invariant(desc.set !== undefined);
return [desc.get, desc.set];
} else if (desc instanceof AbstractJoinedDescriptor) {
return [
desc.joinCondition,
...this._getDescriptorValues(desc.descriptor1),
...this._getDescriptorValues(desc.descriptor2),
];
} else {
invariant(false, "unknown descriptor");
}
}
_deleteProperty(location: BabelNodeLVal): void {
@ -1251,6 +1332,7 @@ export class ResidualHeapSerializer {
if (propertyBinding !== undefined) {
let descriptor = propertyBinding.descriptor;
// "descriptor === undefined" means this array item has been deleted.
invariant(descriptor === undefined || descriptor instanceof PropertyDescriptor);
if (
descriptor !== undefined &&
descriptor.value !== undefined &&
@ -1695,7 +1777,7 @@ export class ResidualHeapSerializer {
// Checks whether a property can be defined via simple assignment, or using object literal syntax.
_canEmbedProperty(obj: ObjectValue, key: string | SymbolValue | AbstractValue, prop: Descriptor): boolean {
if (prop.joinCondition !== undefined) return false;
if (!(prop instanceof PropertyDescriptor)) return false;
let targetDescriptor = this.residualHeapInspector.getTargetIntegrityDescriptor(obj);
@ -1744,7 +1826,8 @@ export class ResidualHeapSerializer {
// TODO #2259: Make deduplication in the face of leaking work for custom accessors
let shouldDropAsAssignedProp = (descriptor: Descriptor | void) =>
isCertainlyLeaked && (descriptor !== undefined && (descriptor.get === undefined && descriptor.set === undefined));
isCertainlyLeaked &&
(descriptor instanceof PropertyDescriptor && (descriptor.get === undefined && descriptor.set === undefined));
if (val.temporalAlias !== undefined) {
return t.objectExpression(props);
@ -1757,7 +1840,8 @@ export class ResidualHeapSerializer {
if (propertyBinding.pathNode !== undefined) continue; // written to inside loop
let descriptor = propertyBinding.descriptor;
if (descriptor === undefined || descriptor.value === undefined) continue; // deleted
if (descriptor === undefined || !(descriptor instanceof PropertyDescriptor) || descriptor.value === undefined)
continue; // deleted
let serializedKey = getAsPropertyNameExpression(key);
if (this._canEmbedProperty(val, key, descriptor)) {

View File

@ -67,6 +67,7 @@ import { createPathConditions, Environment, To } from "../singletons.js";
import { isReactElement, isReactPropsObject, valueIsReactLibraryObject } from "../react/utils.js";
import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js";
import { GeneratorDAG } from "./GeneratorDAG.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type BindingState = {|
capturedBindings: Set<ResidualFunctionBinding>,
@ -321,22 +322,17 @@ export class ResidualHeapVisitor {
if (
!(obj instanceof ArrayValue) &&
!obj.mightNotBeLeakedObject() &&
(descriptor !== undefined && (descriptor.get === undefined && descriptor.set === undefined))
)
(descriptor instanceof PropertyDescriptor && (descriptor.get === undefined && descriptor.set === undefined))
) {
continue;
}
invariant(propertyBindingValue);
this.visitObjectProperty(propertyBindingValue);
}
// inject properties with computed names
if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor;
if (desc !== undefined) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
}
this.visitObjectPropertiesWithComputedNamesDescriptor(obj.unknownProperty.descriptor);
}
// prototype
@ -376,6 +372,22 @@ export class ResidualHeapVisitor {
}
}
visitObjectPropertiesWithComputedNamesDescriptor(desc: void | Descriptor): void {
if (desc !== undefined) {
if (desc instanceof PropertyDescriptor) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
} else if (desc instanceof AbstractJoinedDescriptor) {
this.visitValue(desc.joinCondition);
this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor1);
this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor2);
} else {
invariant(false, "unknown descriptor");
}
}
}
visitObjectPropertiesWithComputedNames(absVal: AbstractValue): void {
if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return;
@ -407,17 +419,19 @@ export class ResidualHeapVisitor {
}
}
visitDescriptor(desc: Descriptor): void {
invariant(desc.value === undefined || desc.value instanceof Value);
if (desc.joinCondition !== undefined) {
visitDescriptor(desc: void | Descriptor): void {
if (desc === undefined) {
} else if (desc instanceof PropertyDescriptor) {
if (desc.value !== undefined) desc.value = this.visitEquivalentValue(desc.value);
if (desc.get !== undefined) this.visitValue(desc.get);
if (desc.set !== undefined) this.visitValue(desc.set);
} else if (desc instanceof AbstractJoinedDescriptor) {
desc.joinCondition = this.visitEquivalentValue(desc.joinCondition);
if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1);
if (desc.descriptor2 !== undefined) this.visitDescriptor(desc.descriptor2);
return;
} else {
invariant(false, "unknown descriptor");
}
if (desc.value !== undefined) desc.value = this.visitEquivalentValue(desc.value);
if (desc.get !== undefined) this.visitValue(desc.get);
if (desc.set !== undefined) this.visitValue(desc.set);
}
visitValueArray(val: ObjectValue): void {

View File

@ -739,15 +739,15 @@ export class ResidualOperationSerializer {
}
_serializeDefineProperty(
{ object, desc }: OperationDescriptorData,
{ object, descriptor }: OperationDescriptorData,
[propName]: Array<BabelNodeExpression>,
context?: SerializationContext
): BabelNodeStatement {
let propString = ((propName: any): BabelNodeStringLiteral).value;
invariant(object !== undefined);
invariant(desc !== undefined);
invariant(descriptor !== undefined);
invariant(context !== undefined);
return context.emitDefinePropertyBody(object, propString, desc);
return context.emitDefinePropertyBody(object, propString, descriptor);
}
_serializeFBMocksMagicGlobalFunction(

View File

@ -37,6 +37,7 @@ import { handleReportedSideEffect } from "./utils.js";
import type { ArgModel } from "../types.js";
import { optionalStringOfLocation } from "../utils/babelhelpers";
import { Properties, Utils } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
type AdditionalFunctionEntry = {
value: ECMAScriptSourceFunctionValue | AbstractValue,
@ -54,7 +55,6 @@ export class Functions {
}
realm: Realm;
// maps back from FunctionValue to the expression string
moduleTracer: ModuleTracer;
writeEffects: WriteEffects;
_noopFunction: void | ECMAScriptSourceFunctionValue;
@ -120,9 +120,9 @@ export class Functions {
for (let funcId of Properties.GetOwnPropertyKeysArray(realm, globalRecordedAdditionalFunctionsMap, true, false)) {
let property = globalRecordedAdditionalFunctionsMap.properties.get(funcId);
if (property) {
let value = property.descriptor && property.descriptor.value;
invariant(property.descriptor instanceof PropertyDescriptor);
let value = property.descriptor.value;
invariant(value !== undefined);
invariant(value instanceof Value);
let entry = this._optimizedFunctionEntryOfValue(value);
if (entry) recordedAdditionalFunctions.push(entry);
}
@ -175,7 +175,7 @@ export class Functions {
}
}
getDeclaringOptimizedFunction(functionValue: ECMAScriptSourceFunctionValue) {
getDeclaringOptimizedFunction(functionValue: ECMAScriptSourceFunctionValue): void | FunctionValue {
for (let [optimizedFunctionValue, additionalEffects] of this.writeEffects) {
// CreatedObjects is all objects created by this optimized function but not
// nested optimized functions.
@ -383,7 +383,7 @@ export class Functions {
if (!location) return; // happens only when accessing an additional function property
if (pbs.has(pb) && !conflicts.has(location)) {
let originalLocation =
pb.descriptor && pb.descriptor.value && !Array.isArray(pb.descriptor.value)
pb.descriptor instanceof PropertyDescriptor && pb.descriptor.value && !Array.isArray(pb.descriptor.value)
? pb.descriptor.value.expressionLocation
: undefined;
let keyString = pb.key instanceof Value ? pb.key.toDisplayString() : pb.key;

View File

@ -36,8 +36,9 @@ import { ResidualHeapRefCounter } from "./ResidualHeapRefCounter";
import { ResidualHeapGraphGenerator } from "./ResidualHeapGraphGenerator";
import { Referentializer } from "./Referentializer.js";
import { Get } from "../methods/index.js";
import { ObjectValue, Value } from "../values/index.js";
import { ObjectValue, Value, FunctionValue } from "../values/index.js";
import { Properties } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
export class Serializer {
constructor(realm: Realm, serializerOptions: SerializerOptions = {}) {
@ -115,7 +116,7 @@ export class Serializer {
for (let name of Properties.GetOwnPropertyKeysArray(realm, output, false, false)) {
let property = output.properties.get(name);
if (!property) continue;
let value = property.descriptor && property.descriptor.value;
let value = property.descriptor instanceof PropertyDescriptor && property.descriptor.value;
if (!(value instanceof Value)) continue;
generator.emitGlobalDeclaration(name, value);
}
@ -125,7 +126,8 @@ export class Serializer {
init(
sourceFileCollection: SourceFileCollection,
sourceMaps?: boolean = false,
onParse?: BabelNodeFile => void
onParse?: BabelNodeFile => void,
onExecute?: (Realm, Map<FunctionValue, Generator>) => void
): void | SerializedResult {
let realmStatistics = this.realm.statistics;
invariant(realmStatistics instanceof SerializerStatistics, "serialization requires SerializerStatistics");
@ -158,6 +160,15 @@ export class Serializer {
});
}
statistics.dumpIR.measure(() => {
if (onExecute !== undefined) {
let optimizedFunctions = new Map();
for (let [functionValue, additionalFunctionEffects] of this.functions.writeEffects)
optimizedFunctions.set(functionValue, additionalFunctionEffects.generator);
onExecute(this.realm, optimizedFunctions);
}
});
statistics.processCollectedNestedOptimizedFunctions.measure(() =>
this.functions.processCollectedNestedOptimizedFunctions(environmentRecordIdAfterGlobalCode)
);

View File

@ -34,6 +34,7 @@ export class SerializerStatistics extends RealmStatistics {
this.referenceCounts = new PerformanceTracker(getTime, getMemory);
this.serializePass = new PerformanceTracker(getTime, getMemory);
this.babelGenerate = new PerformanceTracker(getTime, getMemory);
this.dumpIR = new PerformanceTracker(getTime, getMemory);
}
resetBeforePass(): void {
@ -100,6 +101,7 @@ export class SerializerStatistics extends RealmStatistics {
referenceCounts: PerformanceTracker;
serializePass: PerformanceTracker;
babelGenerate: PerformanceTracker;
dumpIR: PerformanceTracker;
log(): void {
super.log();
@ -136,7 +138,7 @@ export class SerializerStatistics extends RealmStatistics {
this.optimizeReactComponentTreeRoots
)} optimizing react component tree roots, ${format(
this.checkThatFunctionsAreIndependent
)} evaluating functions to optimize`
)} evaluating functions to optimize, ${format(this.dumpIR)} dumping IR`
);
console.log(
`${format(this.deepTraversal)} visiting residual heap, ${format(

View File

@ -30,6 +30,7 @@ import type { AdditionalFunctionEffects } from "./types";
import type { Binding } from "../environment.js";
import type { BabelNodeSourceLocation } from "@babel/types";
import { optionalStringOfLocation } from "../utils/babelhelpers.js";
import { PropertyDescriptor } from "../descriptors.js";
/**
* Get index property list length by searching array properties list for the max index key value plus 1.
@ -126,6 +127,7 @@ export function withDescriptorValue(
func: Function
): void {
if (descriptor !== undefined) {
invariant(descriptor instanceof PropertyDescriptor); // TODO: Handle joined descriptors.
if (descriptor.value !== undefined) {
func(propertyNameOrSymbol, descriptor.value, "value");
} else {
@ -142,8 +144,17 @@ export function withDescriptorValue(
export const ClassPropertiesToIgnore: Set<string> = new Set(["arguments", "name", "caller"]);
export function canIgnoreClassLengthProperty(val: ObjectValue, desc: void | Descriptor, logger: Logger): boolean {
if (desc && desc.value === undefined) {
logger.logError(val, "Functions with length accessor properties are not supported in residual heap.");
if (desc) {
if (desc instanceof PropertyDescriptor) {
if (desc.value === undefined) {
logger.logError(val, "Functions with length accessor properties are not supported in residual heap.");
}
} else {
logger.logError(
val,
"Functions with length properties with different attributes are not supported in residual heap."
);
}
}
return true;
}
@ -170,6 +181,11 @@ export function getObjectPrototypeMetadata(
if (_constructor.descriptor === undefined) {
throw new FatalError("TODO #1024: implement object prototype serialization with deleted constructor");
}
if (!(_constructor.descriptor instanceof PropertyDescriptor)) {
throw new FatalError(
"TODO #1024: implement object prototype serialization with multiple constructor attributes"
);
}
let classFunc = _constructor.descriptor.value;
if (classFunc instanceof ECMAScriptSourceFunctionValue) {
constructor = classFunc;

View File

@ -29,6 +29,7 @@ import type {
} from "./values/index.js";
import { Value } from "./values/index.js";
import { Completion } from "./completions.js";
import type { Descriptor as DescriptorClass } from "./descriptors.js";
import { EnvironmentRecord, LexicalEnvironment, Reference } from "./environment.js";
import { ObjectValue } from "./values/index.js";
import type {
@ -134,26 +135,7 @@ export type DataBlock = Uint8Array;
//
export type Descriptor = {
writable?: boolean,
enumerable?: boolean,
configurable?: boolean,
// If value instanceof EmptyValue, then this descriptor indicates that the
// corresponding property has been deleted.
// Only internal properties (those starting with $ / where internalSlot of owning property binding is true) will ever have array values.
value?: Value | Array<any>,
get?: UndefinedValue | CallableObjectValue | AbstractValue,
set?: UndefinedValue | CallableObjectValue | AbstractValue,
// Only used if the result of a join of two descriptors is not a data descriptor with identical attribute values.
// When present, any update to the property must produce effects that are the join of updating both desriptors,
// using joinCondition as the condition of the join.
joinCondition?: AbstractValue,
descriptor1?: Descriptor,
descriptor2?: Descriptor,
};
export type Descriptor = DescriptorClass;
export type FunctionBodyAstNode = {
// Function body ast node will have uniqueOrderedTag after being interpreted.
@ -399,6 +381,10 @@ export class PathConditions {
return 0;
}
getAssumedConditions(): Set<AbstractValue> {
return new Set();
}
refineBaseConditons(realm: Realm, depth?: number = 0): void {}
}
@ -478,9 +464,7 @@ export type PropertiesType = {
// ECMA262 13.7.5.15
EnumerateObjectProperties(realm: Realm, O: ObjectValue): ObjectValue,
ThrowIfMightHaveBeenDeleted(
value: void | Value | Array<Value> | Array<{ $Key: void | Value, $Value: void | Value }>
): void,
ThrowIfMightHaveBeenDeleted(desc: Descriptor): void,
ThrowIfInternalSlotNotWritable<T: ObjectValue>(realm: Realm, object: T, key: string): T,

View File

@ -29,9 +29,36 @@ import {
import { To } from "../singletons.js";
import invariant from "../invariant.js";
import { Logger } from "./logger.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type TargetIntegrityCommand = "freeze" | "seal" | "preventExtensions" | "";
function hasAnyConfigurable(desc: void | Descriptor): boolean {
if (!desc) {
return false;
}
if (desc instanceof PropertyDescriptor) {
return !!desc.configurable;
}
if (desc instanceof AbstractJoinedDescriptor) {
return hasAnyConfigurable(desc.descriptor1) || hasAnyConfigurable(desc.descriptor2);
}
invariant(false, "internal slots aren't covered here");
}
function hasAnyWritable(desc: void | Descriptor): boolean {
if (!desc) {
return false;
}
if (desc instanceof PropertyDescriptor) {
return desc.value !== undefined && !!desc.writable;
}
if (desc instanceof AbstractJoinedDescriptor) {
return hasAnyWritable(desc.descriptor1) || hasAnyWritable(desc.descriptor2);
}
invariant(false, "internal slots aren't covered here");
}
export class HeapInspector {
constructor(realm: Realm, logger: Logger) {
this.realm = realm;
@ -65,8 +92,8 @@ export class HeapInspector {
for (let propertyBinding of val.properties.values()) {
let desc = propertyBinding.descriptor;
if (desc === undefined) continue; //deleted
if (desc.configurable) anyConfigurable = true;
else if (desc.value !== undefined && desc.writable) anyWritable = true;
if (hasAnyConfigurable(desc)) anyConfigurable = true;
else if (hasAnyWritable(desc)) anyWritable = true;
}
command = anyConfigurable ? "preventExtensions" : anyWritable ? "seal" : "freeze";
}
@ -135,6 +162,12 @@ export class HeapInspector {
}
_canIgnoreProperty(val: ObjectValue, key: string, desc: Descriptor): boolean {
if (!(desc instanceof PropertyDescriptor)) {
// If we have a joined descriptor, there is at least one variant that isn't the same as
// the target descriptor. Since the two descriptors won't be equal.
return false;
}
let targetDescriptor = this.getTargetIntegrityDescriptor(val);
if (IsArray(this.realm, val)) {
@ -242,6 +275,7 @@ export class HeapInspector {
if (prototypeBinding === undefined) return undefined;
let prototypeDesc = prototypeBinding.descriptor;
if (prototypeDesc === undefined) return undefined;
invariant(prototypeDesc instanceof PropertyDescriptor);
invariant(prototypeDesc.value === undefined || prototypeDesc.value instanceof Value);
return prototypeDesc.value;
}

666
src/utils/TextPrinter.js Normal file
View File

@ -0,0 +1,666 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import {
Completion,
SimpleNormalCompletion,
ThrowCompletion,
JoinedNormalAndAbruptCompletions,
} from "../completions.js";
import type { Realm, Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js";
import { PropertyDescriptor, InternalSlotDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
import {
EnvironmentRecord,
type Binding,
type LexicalEnvironment,
DeclarativeEnvironmentRecord,
FunctionEnvironmentRecord,
ObjectEnvironmentRecord,
GlobalEnvironmentRecord,
} from "../environment.js";
import {
PrimitiveValue,
AbstractValue,
ObjectValue,
FunctionValue,
ECMAScriptSourceFunctionValue,
NativeFunctionValue,
BoundFunctionValue,
SymbolValue,
ProxyValue,
Value,
UndefinedValue,
} from "../values/index.js";
import invariant from "../invariant.js";
import {
Printer,
Generator,
type OperationDescriptorType,
type CustomGeneratorEntryType,
type OperationDescriptorData,
} from "./generator.js";
import * as t from "@babel/types";
const indent = " ";
export class TextPrinter implements Printer {
constructor(
printLine: string => void,
abstractValueIds?: Map<AbstractValue, number> = new Map(),
symbolIds?: Map<SymbolValue, number> = new Map()
) {
this._printLine = printLine;
this._abstractValueIds = abstractValueIds;
this._symbolIds = symbolIds;
this._indent = "";
this._objects = new Set();
this._propertyBindings = new Set();
this._environmentRecords = new Set();
this._bindings = new Set();
this._lexicalEnvironments = new Set();
this._symbols = new Set();
}
_printLine: string => void;
_abstractValueIds: Map<AbstractValue, number>;
_symbolIds: Map<SymbolValue, number>;
_indent: string;
_objects: Set<ObjectValue>;
_propertyBindings: Set<PropertyBinding>;
_environmentRecords: Set<EnvironmentRecord>;
_bindings: Set<Binding>;
_lexicalEnvironments: Set<LexicalEnvironment>;
_symbols: Set<SymbolValue>;
_nest(): void {
this._indent += indent;
}
_unnest(): void {
this._indent = this._indent.substring(0, this._indent.length - indent.length);
}
_print(text: string): void {
this._printLine(this._indent + text);
}
_printDefinition(id: string, constructorName: string, args: Array<string>) {
this._print(`* ${id} = ${constructorName}(${args.join(", ")})`);
}
printGeneratorEntry(
declared: void | AbstractValue | ObjectValue,
type: OperationDescriptorType | CustomGeneratorEntryType,
args: Array<Value>,
data: OperationDescriptorData,
metadata: { isPure: boolean, mutatesOnly: void | Array<Value> }
): void {
switch (type) {
case "DO_WHILE":
invariant(data.value !== undefined);
this._print(`do while ${this.describeValue(data.value)}`);
this._nest();
const generator = data.generator;
invariant(generator !== undefined);
this.printGenerator(generator, "body");
this._unnest();
break;
case "JOIN_GENERATORS":
invariant(args.length === 1);
this._print(`if ${this.describeValue(args[0])}`);
this._nest();
const generators = data.generators;
invariant(generators !== undefined && generators.length === 2);
this.printGenerator(generators[0], "then");
this.printGenerator(generators[1], "else");
this._unnest();
break;
default:
let text;
if (declared !== undefined) {
invariant(declared.intrinsicName !== undefined);
text = `${this.describeExpression(declared.intrinsicName)} := `;
} else {
text = "";
}
text += type;
const dataTexts = [];
if (data.unaryOperator !== undefined) dataTexts.push(`unary ${data.unaryOperator}`); // used by UNARY_EXPRESSION
if (data.binaryOperator !== undefined) dataTexts.push(`binary ${data.binaryOperator}`); // used by BINARY_EXPRESSION
if (data.logicalOperator !== undefined) dataTexts.push(`logical ${data.logicalOperator}`); // used by LOGICAL_EXPRESSION
if (data.incrementor !== undefined) dataTexts.push(`incrementor ${data.incrementor}`); // used by UPDATE_INCREMENTOR
if (data.prefix !== undefined) dataTexts.push("prefix"); // used by UNARY_EXPRESSION
if (data.binding !== undefined) dataTexts.push(`binding ${this.describeBinding(data.binding)}`); // used by GET_BINDING
if (data.propertyBinding !== undefined)
dataTexts.push(`property binding ${this.describePropertyBinding(data.propertyBinding)}`); // used by LOGICAL_PROPERTY_ASSIGNMENT
if (data.object !== undefined) dataTexts.push(`object ${this.describeValue(data.object)}`); // used by DEFINE_PROPERTY
if (data.descriptor !== undefined) dataTexts.push(`desc ${this.describeDescriptor(data.descriptor)}`); // used by DEFINE_PROPERTY
if (data.value !== undefined) dataTexts.push(`value ${this.describeValue(data.value)}`); // used by DO_WHILE, CONDITIONAL_PROPERTY_ASSIGNMENT, LOGICAL_PROPERTY_ASSIGNMENT, LOCAL_ASSIGNMENT, CONDITIONAL_THROW, EMIT_PROPERTY_ASSIGNMENT
if (data.id !== undefined) dataTexts.push(`id ${this.describeExpression(data.id)}`); // used by IDENTIFIER
if (data.thisArg !== undefined) dataTexts.push(`this arg ${this.describeBaseValue(data.thisArg)}`); // used by CALL_BAILOUT
if (data.propRef !== undefined) dataTexts.push(`prop ref ${this.describeKey(data.propRef)}`); // used by CALL_BAILOUT, and then only if string
if (data.state !== undefined) dataTexts.push(`state ${data.state}`); // used by PROPERTY_INVARIANT
if (data.usesThis !== undefined) dataTexts.push(`usesThis`); // used by FOR_STATEMENT_FUNC
if (data.path !== undefined) dataTexts.push(`path ${this.describeValue(data.path)}`); // used by PROPERTY_ASSIGNMENT, CONDITIONAL_PROPERTY_ASSIGNMENT
if (data.callFunctionRef !== undefined)
dataTexts.push(`call function ref ${this.describeExpression(data.callFunctionRef)}`); // used by EMIT_CALL and EMIT_CALL_AND_CAPTURE_RESULT
if (data.templateSource !== undefined)
dataTexts.push(`template source ${this.describeExpression(data.templateSource)}`); // used by ABSTRACT_FROM_TEMPLATE
if (data.propertyGetter !== undefined) dataTexts.push(`property getter ${data.propertyGetter}`); // used by ABSTRACT_OBJECT_GET
// TODO:
// appendLastToInvariantOperationDescriptor?: OperationDescriptor, // used by INVARIANT
// concreteComparisons?: Array<Value>, // used by FULL_INVARIANT_ABSTRACT
// boundName?: BabelNodeIdentifier, // used by FOR_IN
// lh?: BabelNodeVariableDeclaration, // used by FOR_IN
// quasis?: Array<BabelNodeTemplateElement>, // used by REACT_SSR_TEMPLATE_LITERAL
// typeComparisons?: Set<typeof Value>, // used by FULL_INVARIANT_ABSTRACT
// violationConditionOperationDescriptor?: OperationDescriptor, // used by INVARIANT
if (dataTexts.length > 0) text += `<${dataTexts.join("; ")}>`;
if (args.length > 0) text += `(${this.describeValues(args)})`;
const metadataTexts = [];
if (metadata.isPure) metadataTexts.push("isPure");
if (metadata.mutatesOnly !== undefined && metadata.mutatesOnly.length > 0)
metadataTexts.push(`mutates only: [${this.describeValues(metadata.mutatesOnly)}]`);
if (metadataTexts.length > 0) text += `[${metadataTexts.join("; ")}]`;
this._print(text);
break;
}
}
printGenerator(generator: Generator, label?: string = "(entry point)"): void {
this._print(`${label}: ${JSON.stringify(generator.getName())}`);
this._nest();
if (generator.pathConditions.getLength() > 0)
this._print(
`path conditions ${this.describeValues(Array.from(generator.pathConditions.getAssumedConditions()))}`
);
generator.print(this);
this._unnest();
}
print(realm: Realm, optimizedFunctions: Map<FunctionValue, Generator>): void {
const realmGenerator = realm.generator;
if (realmGenerator !== undefined) this.printGenerator(realmGenerator);
for (const [functionValue, generator] of optimizedFunctions) {
const effectsToApply = generator.effectsToApply;
invariant(effectsToApply !== undefined);
this._print(`=== optimized function ${this.describeValue(functionValue)}`);
realm.withEffectsAppliedInGlobalEnv(effects => {
const nestedPrinter = new TextPrinter(this._printLine, this._abstractValueIds, this._symbolIds);
nestedPrinter.printEffects(effects, generator);
return nestedPrinter; // not needed, but withEffectsAppliedInGlobalEnv has an unmotivated invariant that the result must not be undefined
}, effectsToApply);
}
}
describeCompletion(result: Completion): string {
const args = [];
if (result instanceof SimpleNormalCompletion) args.push(`value ${this.describeValue(result.value)}`);
else if (result instanceof ThrowCompletion) args.push(`value ${this.describeValue(result.value)}`);
else {
invariant(result instanceof JoinedNormalAndAbruptCompletions);
args.push(`join condition ${this.describeValue(result.joinCondition)}`);
args.push(`consequent ${this.describeCompletion(result.consequent)}`);
args.push(`alternate ${this.describeCompletion(result.alternate)}`);
if (result.composedWith !== undefined) args.push(`composed with ${this.describeCompletion(result.composedWith)}`);
}
return `${result.constructor.name}(${args.join(", ")})`;
}
printEffects(effects: Effects, generator?: Generator): void {
this._nest();
this.printGenerator(generator || effects.generator);
// skip effects.generator
if (effects.modifiedProperties.size > 0)
this._print(
`modified property bindings: [${Array.from(effects.modifiedProperties.keys())
.map(propertyBinding => this.describePropertyBinding(propertyBinding))
.join(", ")}]`
);
if (effects.modifiedBindings.size > 0)
this._print(
`modified bindings: [${Array.from(effects.modifiedBindings.keys())
.map(binding => this.describeBinding(binding))
.join(", ")}]`
);
if (effects.createdObjects.size > 0)
this._print(
`created objects: [${Array.from(effects.createdObjects)
.map(object => this.describeValue(object))
.join(", ")}]`
);
if (!(effects.result instanceof UndefinedValue)) this._print(`result: ${this.describeCompletion(effects.result)}`);
this._unnest();
}
describeExpression(expression: string): string {
if (t.isValidIdentifier(expression)) return expression;
else return "@" + JSON.stringify(expression);
}
describeValues<V: Value>(values: Array<V>): string {
return values.map(value => this.describeValue(value)).join(", ");
}
abstractValueName(value: AbstractValue): string {
const id = this._abstractValueIds.get(value);
invariant(id !== undefined);
return `value#${id}`;
}
printAbstractValue(value: AbstractValue): void {
invariant(value.intrinsicName === undefined);
let kind = value.kind;
// TODO: I'd expect kind to be defined in this situation; however, it's not defined for test ForInStatement4.js
// invariant(kind !== undefined);
if (kind === undefined) kind = "(no kind)";
this._printDefinition(
this.abstractValueName(value),
this.describeExpression(kind),
value.args.map(arg => this.describeValue(arg))
);
}
objectValueName(value: ObjectValue): string {
invariant(this._objects.has(value));
let name;
if (value instanceof FunctionValue) name = "func";
else if (value instanceof ProxyValue) name = "proxy";
else name = "object";
return `${name}#${value.getHash()}`;
}
printObjectValue(value: ObjectValue): void {
const args = [];
if (value.temporalAlias !== undefined) args.push(`temporalAlias ${this.describeValue(value.temporalAlias)}`);
if (value instanceof FunctionValue) {
if (value instanceof NativeFunctionValue) {
// TODO: This shouldn't happen; all native function values should be intrinsics
} else if (value instanceof BoundFunctionValue) {
args.push(`$BoundTargetFunction ${this.describeValue(value.$BoundTargetFunction)}`);
args.push(`$BoundThis ${this.describeValue(value.$BoundThis)}`);
args.push(`$BoundArguments [${this.describeValues(value.$BoundArguments)}]`);
} else {
invariant(value instanceof ECMAScriptSourceFunctionValue);
args.push(`$ConstructorKind ${value.$ConstructorKind}`);
args.push(`$ThisMode ${value.$ThisMode}`);
args.push(`$FunctionKind ${value.$FunctionKind}`);
if (value.$HomeObject !== undefined) args.push(`$HomeObject ${this.describeValue(value.$HomeObject)}`);
// TODO: $Strict should always be defined according to its flow type signature, however, there are some tests where it's not
if (value.$Strict) args.push(`$Strict`);
args.push(`$FormalParameters ${value.$FormalParameters.length}`);
// TODO: pretty-print $ECMAScriptCode
// TODO: $Environment should always be defined according to its flow type signature, however, it's not in test ConcreteModel2.js
if (value.$Environment) args.push(`$Environment ${this.describeLexicalEnvironment(value.$Environment)}`);
}
} else if (value instanceof ProxyValue) {
args.push(`$ProxyTarget ${this.describeValue(value.$ProxyTarget)}`);
args.push(`$ProxyHandler ${this.describeValue(value.$ProxyHandler)}`);
} else {
const kind = value.getKind();
if (kind !== "Object") args.push(`kind ${kind}`);
switch (kind) {
case "RegExp":
const originalSource = value.$OriginalSource;
invariant(originalSource !== undefined);
args.push(`$OriginalSource ${originalSource}`);
const originalFlags = value.$OriginalFlags;
invariant(originalFlags !== undefined);
args.push(`$OriginalFlags ${originalFlags}`);
break;
case "Number":
const numberData = value.$NumberData;
invariant(numberData !== undefined);
args.push(`$NumberData ${this.describeValue(numberData)}`);
break;
case "String":
const stringData = value.$StringData;
invariant(stringData !== undefined);
args.push(`$StringData ${this.describeValue(stringData)}`);
break;
case "Boolean":
const booleanData = value.$BooleanData;
invariant(booleanData !== undefined);
args.push(`$BooleanData ${this.describeValue(booleanData)}`);
break;
case "Date":
const dateValue = value.$DateValue;
invariant(dateValue !== undefined);
args.push(`$DateValue ${this.describeValue(dateValue)}`);
break;
case "ArrayBuffer":
const len = value.$ArrayBufferByteLength;
invariant(len !== undefined);
args.push(`$ArrayBufferByteLength ${len}`);
const db = value.$ArrayBufferData;
invariant(db !== undefined);
if (db !== null) args.push(`$ArrayBufferData [${db.join(", ")}]`);
break;
case "Float32Array":
case "Float64Array":
case "Int8Array":
case "Int16Array":
case "Int32Array":
case "Uint8Array":
case "Uint16Array":
case "Uint32Array":
case "Uint8ClampedArray":
case "DataView":
const buf = value.$ViewedArrayBuffer;
invariant(buf !== undefined);
args.push(`$ViewedArrayBuffer ${this.describeValue(buf)}`);
break;
case "Map":
const mapDataEntries = value.$MapData;
invariant(mapDataEntries !== undefined);
args.push(`$MapData [${this.describeMapEntries(mapDataEntries)}]`);
break;
case "WeakMap":
const weakMapDataEntries = value.$WeakMapData;
invariant(weakMapDataEntries !== undefined);
args.push(`$WeakMapData [${this.describeMapEntries(weakMapDataEntries)}]`);
break;
case "Set":
const setDataEntries = value.$SetData;
invariant(setDataEntries !== undefined);
args.push(`$SetData [${this.describeSetEntries(setDataEntries)}]`);
break;
case "WeakSet":
const weakSetDataEntries = value.$WeakSetData;
invariant(weakSetDataEntries !== undefined);
args.push(`$WeakSetData [${this.describeSetEntries(weakSetDataEntries)}]`);
break;
case "ReactElement":
case "Object":
case "Array":
break;
default:
invariant(false);
}
}
// properties
if (value.properties.size > 0) {
args.push(
`properties [${Array.from(value.properties.keys())
.map(key => this.describeKey(key))
.join(", ")}]`
);
}
// symbols
if (value.symbols.size > 0) {
args.push(
`symbols [${Array.from(value.symbols.keys())
.map(key => this.describeKey(key))
.join(", ")}]`
);
}
const unknownProperty = value.unknownProperty;
if (unknownProperty !== undefined) args.push(`unknown property`);
if (value.$Prototype !== undefined) args.push(`$Prototype ${this.describeValue(value.$Prototype)}`);
this._printDefinition(this.objectValueName(value), value.constructor.name, args);
// jull pull on property bindings to get them emitting
for (const propertyBinding of value.properties.values()) this.describePropertyBinding(propertyBinding);
for (const propertyBinding of value.symbols.values()) this.describePropertyBinding(propertyBinding);
if (unknownProperty !== undefined) this.describePropertyBinding(unknownProperty);
}
describeValue(value: Value): string {
if (value.intrinsicName !== undefined) return this.describeExpression(value.intrinsicName);
if (value instanceof SymbolValue) return this.describeSymbol(value);
if (value instanceof PrimitiveValue) return value.toDisplayString();
if (value instanceof ObjectValue) {
if (!this._objects.has(value)) {
this._objects.add(value);
this.printObjectValue(value);
}
return this.objectValueName(value);
} else {
invariant(value instanceof AbstractValue, value.constructor.name);
if (!this._abstractValueIds.has(value)) {
this._abstractValueIds.set(value, this._abstractValueIds.size);
this.printAbstractValue(value);
}
return this.abstractValueName(value);
}
}
describeMapEntries(entries: Array<{ $Key: void | Value, $Value: void | Value }>): string {
return entries
.map(entry => {
const args = [];
if (entry.$Key !== undefined) args.push(`$Key ${this.describeValue(entry.$Key)}`);
if (entry.$Value !== undefined) args.push(`$Value ${this.describeValue(entry.$Value)}`);
return `{${args.join(", ")}}`;
})
.join(", ");
}
describeSetEntries(entries: Array<void | Value>): string {
return entries.map(entry => (entry === undefined ? "(undefined)" : this.describeValue(entry))).join(", ");
}
describeDescriptor(desc: Descriptor): string {
if (desc instanceof PropertyDescriptor) return this.describePropertyDescriptor(desc);
else if (desc instanceof InternalSlotDescriptor) return this.describeInternalSlotDescriptor(desc);
else {
invariant(desc instanceof AbstractJoinedDescriptor, desc.constructor.name);
return this.describeAbstractJoinedDescriptor(desc);
}
}
describePropertyDescriptor(desc: PropertyDescriptor): string {
const args = [];
if (desc.writable) args.push("writable");
if (desc.enumerable) args.push("enumerable");
if (desc.configurable) args.push("configurable");
if (desc.value !== undefined) args.push(`value ${this.describeValue(desc.value)}`);
if (desc.get !== undefined) args.push(`get ${this.describeValue(desc.get)}`);
if (desc.set !== undefined) args.push(`set ${this.describeValue(desc.set)}`);
return `PropertyDescriptor(${args.join(", ")})`;
}
describeInternalSlotDescriptor(desc: InternalSlotDescriptor): string {
const args = [];
if (desc.value instanceof Value) args.push(`value ${this.describeValue(desc.value)}`);
else if (Array.isArray(desc.value)) args.push(`some array`); // TODO
return `InternalSlotDescriptor(${args.join(", ")})`;
}
describeAbstractJoinedDescriptor(desc: AbstractJoinedDescriptor): string {
const args = [];
args.push(`join condition ${this.describeValue(desc.joinCondition)}`);
if (desc.descriptor1 !== undefined) args.push(`descriptor1 ${this.describeDescriptor(desc.descriptor1)}`);
if (desc.descriptor2 !== undefined) args.push(`descriptor2 ${this.describeDescriptor(desc.descriptor2)}`);
return `AbstractJoinedDescriptor(${args.join(", ")})`;
}
bindingName(binding: Binding): string {
invariant(this._bindings.has(binding));
return `${this.describeEnvironmentRecord(binding.environment)}.${this.describeExpression(binding.name)}`;
}
printBinding(binding: Binding): void {
const args = [];
if (binding.isGlobal) args.push("is global");
if (binding.mightHaveBeenCaptured) args.push("might have been captured");
if (binding.initialized) args.push("initialized");
if (binding.mutable) args.push("mutable");
if (binding.deletable) args.push("deletable");
if (binding.strict) args.push("strict");
if (binding.hasLeaked) args.push("has leaked");
if (binding.value !== undefined) args.push(`value ${this.describeValue(binding.value)})`);
if (binding.phiNode !== undefined) args.push(`phi node ${this.describeValue(binding.phiNode)}`);
this._printDefinition(this.bindingName(binding), "Binding", args);
}
describeBinding(binding: Binding): string {
if (!this._bindings.has(binding)) {
this._bindings.add(binding);
this.printBinding(binding);
}
return this.bindingName(binding);
}
describeKey(key: void | string | Value): string {
if (key === undefined) return "(undefined)";
else if (typeof key === "string") return this.describeExpression(key);
else {
invariant(key instanceof Value);
return this.describeValue(key);
}
}
propertyBindingName(propertyBinding: PropertyBinding): string {
return `${this.describeValue(propertyBinding.object)}.${this.describeKey(propertyBinding.key)}`;
}
printPropertyBinding(propertyBinding: PropertyBinding): void {
const args = [];
if (propertyBinding.internalSlot) args.push("internal slot");
if (propertyBinding.descriptor !== undefined)
args.push(`descriptor ${this.describeDescriptor(propertyBinding.descriptor)}`);
if (propertyBinding.pathNode !== undefined) args.push(`path node ${this.describeValue(propertyBinding.pathNode)}`);
this._printDefinition(this.propertyBindingName(propertyBinding), "PropertyBinding", args);
}
describePropertyBinding(propertyBinding: PropertyBinding): string {
if (!this._propertyBindings.has(propertyBinding)) {
this._propertyBindings.add(propertyBinding);
this.printPropertyBinding(propertyBinding);
}
return this.propertyBindingName(propertyBinding);
}
environmentRecordName(environment: EnvironmentRecord): string {
invariant(this._environmentRecords.has(environment));
let name;
if (environment instanceof DeclarativeEnvironmentRecord) {
name = environment instanceof FunctionEnvironmentRecord ? "funEnv" : "declEnv";
} else if (environment instanceof ObjectEnvironmentRecord) {
name = "objEnv";
} else {
invariant(environment instanceof GlobalEnvironmentRecord);
name = "globEnv";
}
return `${name}#${environment.id}`;
}
printEnvironmentRecord(environment: EnvironmentRecord): void {
const args = [];
if (environment instanceof DeclarativeEnvironmentRecord) {
if (environment instanceof FunctionEnvironmentRecord) {
args.push(`$ThisBindingStatus ${environment.$ThisBindingStatus}`);
// TODO: $ThisValue should always be defined according to its flow type signature, however, it's not for test ObjectAssign9.js
if (environment.$ThisValue !== undefined) args.push(`$ThisValue ${this.describeValue(environment.$ThisValue)}`);
if (environment.$HomeObject !== undefined)
args.push(`$HomeObject ${this.describeValue(environment.$HomeObject)}`);
args.push(`$FunctionObject ${this.describeValue(environment.$FunctionObject)}`);
}
if (environment.$NewTarget !== undefined) args.push(`$NewTarget ${this.describeValue(environment.$NewTarget)}`);
if (environment.frozen) args.push("frozen");
const bindings = Object.keys(environment.bindings);
if (bindings.length > 0)
args.push(
`bindings [${Object.keys(environment.bindings)
.map(key => this.describeKey(key))
.join(", ")}]`
);
} else if (environment instanceof ObjectEnvironmentRecord) {
args.push(`object ${this.describeValue(environment.object)}`);
if (environment.withEnvironment) args.push("with environment");
} else if (environment instanceof GlobalEnvironmentRecord) {
args.push(`$DeclarativeRecord ${this.describeEnvironmentRecord(environment.$DeclarativeRecord)}`);
args.push(`$ObjectRecord ${this.describeEnvironmentRecord(environment.$DeclarativeRecord)}`);
if (environment.$VarNames.length > 0)
args.push(`$VarNames [${environment.$VarNames.map(varName => this.describeExpression(varName)).join(", ")}]`);
args.push(`$GlobalThisValue ${this.describeValue(environment.$GlobalThisValue)}`);
}
this._printDefinition(this.environmentRecordName(environment), environment.constructor.name, args);
// pull on bindings to get them emitted
if (environment instanceof DeclarativeEnvironmentRecord)
for (const bindingName in environment.bindings) this.describeBinding(environment.bindings[bindingName]);
}
describeEnvironmentRecord(environment: EnvironmentRecord): string {
if (!this._environmentRecords.has(environment)) {
this._environmentRecords.add(environment);
this.printEnvironmentRecord(environment);
}
return this.environmentRecordName(environment);
}
describeBaseValue(value: void | EnvironmentRecord | Value): string {
if (value === undefined) return "(undefined)";
else if (value instanceof Value) return this.describeValue(value);
invariant(value instanceof EnvironmentRecord);
return this.describeEnvironmentRecord(value);
}
lexicalEnvironmentName(environment: LexicalEnvironment): string {
invariant(this._lexicalEnvironments.has(environment));
return `lexEnv#${environment._uid}`;
}
printLexicalEnvironment(environment: LexicalEnvironment): void {
const args = [];
if (environment.destroyed) args.push("destroyed");
if (environment.parent !== null) args.push(`parent ${this.describeLexicalEnvironment(environment.parent)}`);
args.push(`environment record ${this.describeEnvironmentRecord(environment.environmentRecord)}`);
this._printDefinition(this.lexicalEnvironmentName(environment), "LexicalEnvironment", args);
}
describeLexicalEnvironment(environment: LexicalEnvironment): string {
if (!this._lexicalEnvironments.has(environment)) {
this._lexicalEnvironments.add(environment);
this.printLexicalEnvironment(environment);
}
return this.lexicalEnvironmentName(environment);
}
symbolName(symbol: SymbolValue): string {
const id = this._symbolIds.get(symbol);
invariant(id !== undefined);
return `symbol#${id}`;
}
printSymbol(symbol: SymbolValue): void {
const args = [];
if (symbol.$Description) args.push(`$Description ${this.describeValue(symbol.$Description)}`);
this._printDefinition(this.symbolName(symbol), "Symbol", args);
}
describeSymbol(symbol: SymbolValue): string {
if (!this._symbolIds.has(symbol)) {
this._symbolIds.set(symbol, this._symbolIds.size);
}
if (!this._symbols.has(symbol)) {
this._symbols.add(symbol);
this.printSymbol(symbol);
}
return this.symbolName(symbol);
}
}

View File

@ -56,6 +56,7 @@ import { concretize, Join, Utils } from "../singletons.js";
import type { SerializerOptions } from "../options.js";
import type { PathConditions, ShapeInformationInterface } from "../types.js";
import { PreludeGenerator } from "./PreludeGenerator.js";
import { PropertyDescriptor } from "../descriptors.js";
export type OperationDescriptorType =
| "ABSTRACT_FROM_TEMPLATE"
@ -147,7 +148,7 @@ export type OperationDescriptorData = {
boundName?: BabelNodeIdentifier, // used by FOR_IN
callFunctionRef?: string, // used by EMIT_CALL and EMIT_CALL_AND_CAPTURE_RESULT
concreteComparisons?: Array<Value>, // used by FULL_INVARIANT_ABSTRACT
desc?: Descriptor, // used by DEFINE_PROPERTY
descriptor?: Descriptor, // used by DEFINE_PROPERTY
generator?: Generator, // used by DO_WHILE
generators?: Array<Generator>, // used by JOIN_GENERATORS
id?: string, // used by IDENTIFIER
@ -225,6 +226,19 @@ export type VisitEntryCallbacks = {|
visitBindingAssignment: (Binding, Value) => Value,
|};
export type CustomGeneratorEntryType = "MODIFIED_PROPERTY" | "MODIFIED_BINDING" | "RETURN" | "BINDING_ASSIGNMENT";
export interface Printer {
printGeneratorEntry(
declared: void | AbstractValue | ObjectValue,
type: OperationDescriptorType | CustomGeneratorEntryType,
args: Array<Value>,
data: OperationDescriptorData,
metadata: { isPure: boolean, mutatesOnly: void | Array<Value> }
): void;
printGenerator(generator: Generator, label?: string): void;
}
export class GeneratorEntry {
constructor(realm: Realm) {
// We increment the index of every TemporalOperationEntry created.
@ -236,6 +250,10 @@ export class GeneratorEntry {
this.index = realm.temporalEntryCounter++;
}
print(printer: Printer): void {
invariant(false, "GeneratorEntry is an abstract base class");
}
visit(callbacks: VisitEntryCallbacks, containingGenerator: Generator): boolean {
invariant(false, "GeneratorEntry is an abstract base class");
}
@ -263,7 +281,6 @@ export type TemporalOperationEntryArgs = {
declared?: AbstractValue | ObjectValue,
args: Array<Value>,
operationDescriptor: OperationDescriptor,
dependencies?: Array<Generator>,
isPure?: boolean,
mutatesOnly?: Array<Value>,
};
@ -284,10 +301,17 @@ export class TemporalOperationEntry extends GeneratorEntry {
declared: void | AbstractValue | ObjectValue;
args: Array<Value>;
operationDescriptor: OperationDescriptor;
dependencies: void | Array<Generator>;
isPure: void | boolean;
mutatesOnly: void | Array<Value>;
print(printer: Printer): void {
const operationDescriptor = this.operationDescriptor;
printer.printGeneratorEntry(this.declared, operationDescriptor.type, this.args, this.operationDescriptor.data, {
isPure: !!this.isPure,
mutatesOnly: this.mutatesOnly,
});
}
toDisplayJson(depth: number): DisplayResult {
if (depth <= 0) return `TemporalOperation${this.index}`;
let obj = { type: "TemporalOperation", ...this };
@ -345,8 +369,9 @@ export class TemporalOperationEntry extends GeneratorEntry {
}
}
}
if (this.dependencies)
for (let dependency of this.dependencies) callbacks.visitGenerator(dependency, containingGenerator);
let dependencies = this.getDependencies();
if (dependencies !== undefined)
for (let dependency of dependencies) callbacks.visitGenerator(dependency, containingGenerator);
return true;
}
}
@ -393,7 +418,19 @@ export class TemporalOperationEntry extends GeneratorEntry {
}
getDependencies(): void | Array<Generator> {
return this.dependencies;
const operationDescriptor = this.operationDescriptor;
switch (operationDescriptor.type) {
case "DO_WHILE":
let generator = operationDescriptor.data.generator;
invariant(generator !== undefined);
return [generator];
case "JOIN_GENERATORS":
let generators = operationDescriptor.data.generators;
invariant(generators !== undefined);
return generators;
default:
return undefined;
}
}
}
@ -445,6 +482,16 @@ class ModifiedPropertyEntry extends GeneratorEntry {
propertyBinding: PropertyBinding;
newDescriptor: void | Descriptor;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"MODIFIED_PROPERTY",
[],
{ descriptor: this.newDescriptor, propertyBinding: this.propertyBinding },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string {
let propertyKey = this.propertyBinding.key;
let propertyKeyString = propertyKey instanceof Value ? propertyKey.toDisplayString() : propertyKey;
@ -488,6 +535,16 @@ class ModifiedBindingEntry extends GeneratorEntry {
containingGenerator: Generator;
modifiedBinding: Binding;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"MODIFIED_BINDING",
[],
{ binding: this.modifiedBinding, value: this.modifiedBinding.value },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string {
return `[ModifiedBinding ${this.modifiedBinding.name}]`;
}
@ -520,6 +577,10 @@ class ReturnValueEntry extends GeneratorEntry {
returnValue: Value;
containingGenerator: Generator;
print(printer: Printer): void {
printer.printGeneratorEntry(undefined, "RETURN", [this.returnValue], {}, { isPure: false, mutatesOnly: undefined });
}
toDisplayString(): string {
return `[Return ${this.returnValue.toDisplayString()}]`;
}
@ -552,6 +613,16 @@ class BindingAssignmentEntry extends GeneratorEntry {
binding: Binding;
value: Value;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"BINDING_ASSIGNMENT",
[this.value],
{ binding: this.binding },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string {
return `[BindingAssignment ${this.binding.name} = ${this.value.toDisplayString()}]`;
}
@ -592,6 +663,10 @@ export class Generator {
_name: string;
pathConditions: PathConditions;
print(printer: Printer): void {
for (let entry of this._entries) entry.print(printer);
}
toDisplayString(): string {
return Utils.jsonToDisplayString(this, 2);
}
@ -664,16 +739,16 @@ export class Generator {
emitPropertyModification(propertyBinding: PropertyBinding): void {
invariant(this.effectsToApply !== undefined);
let desc = propertyBinding.descriptor;
if (desc !== undefined) {
if (desc !== undefined && desc instanceof PropertyDescriptor) {
let value = desc.value;
if (value instanceof AbstractValue) {
if (value.kind === "conditional") {
let [c, x, y] = value.args;
if (c instanceof AbstractValue && c.kind === "template for property name condition") {
let ydesc = Object.assign({}, desc, { value: y });
let ydesc = new PropertyDescriptor(Object.assign({}, (desc: any), { value: y }));
let yprop = Object.assign({}, propertyBinding, { descriptor: ydesc });
this.emitPropertyModification(yprop);
let xdesc = Object.assign({}, desc, { value: x });
let xdesc = new PropertyDescriptor(Object.assign({}, (desc: any), { value: x }));
let key = c.args[0];
invariant(key instanceof AbstractValue);
let xprop = Object.assign({}, propertyBinding, { key, descriptor: xdesc });
@ -759,14 +834,14 @@ export class Generator {
});
}
emitDefineProperty(object: ObjectValue, key: string, desc: Descriptor, isDescChanged: boolean = true): void {
emitDefineProperty(object: ObjectValue, key: string, desc: PropertyDescriptor, isDescChanged: boolean = true): void {
if (object.refuseSerialization) return;
if (desc.enumerable && desc.configurable && desc.writable && desc.value && !isDescChanged) {
let descValue = desc.value;
invariant(descValue instanceof Value);
this.emitPropertyAssignment(object, key, descValue);
} else {
desc = Object.assign({}, desc);
desc = new PropertyDescriptor(desc);
let descValue = desc.value || object.$Realm.intrinsics.undefined;
invariant(descValue instanceof Value);
this._addEntry({
@ -777,7 +852,7 @@ export class Generator {
desc.get || object.$Realm.intrinsics.undefined,
desc.set || object.$Realm.intrinsics.undefined,
],
operationDescriptor: createOperationDescriptor("DEFINE_PROPERTY", { object, desc }),
operationDescriptor: createOperationDescriptor("DEFINE_PROPERTY", { object, descriptor: desc }),
});
}
}
@ -812,7 +887,6 @@ export class Generator {
this._addEntry({
args: [],
operationDescriptor: createOperationDescriptor("DO_WHILE", { generator: body, value: test }),
dependencies: [body],
});
}
@ -1137,7 +1211,6 @@ export class Generator {
this._addEntry({
args: [joinCondition],
operationDescriptor: createOperationDescriptor("JOIN_GENERATORS", { generators }),
dependencies: generators,
});
}
}

View File

@ -41,6 +41,7 @@ import invariant from "../invariant.js";
import { HeapInspector } from "../utils/HeapInspector.js";
import { Logger } from "../utils/logger.js";
import { isReactElement } from "../react/utils.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type LeakedFunctionInfo = {
unboundReads: Set<string>,
@ -137,6 +138,7 @@ function materializeObject(realm: Realm, object: ObjectValue, getCachingHeapInsp
// If it indeed means deleted binding, should we initialize descriptor with a deleted value?
if (generator !== undefined) generator.emitPropertyDelete(object, name);
} else {
invariant(descriptor instanceof PropertyDescriptor); // TODO: Deal with joined descriptors.
let value = descriptor.value;
invariant(
value === undefined || value instanceof Value,
@ -216,11 +218,7 @@ class ObjectValueLeakingVisitor {
// inject properties with computed names
if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor;
if (desc !== undefined) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
}
this.visitObjectPropertiesWithComputedNamesDescriptor(desc);
}
// prototype
@ -258,6 +256,22 @@ class ObjectValueLeakingVisitor {
this.visitValue(proto);
}
visitObjectPropertiesWithComputedNamesDescriptor(desc: void | Descriptor): void {
if (desc !== undefined) {
if (desc instanceof PropertyDescriptor) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
} else if (desc instanceof AbstractJoinedDescriptor) {
this.visitValue(desc.joinCondition);
this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor1);
this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor2);
} else {
invariant(false, "unknown descriptor");
}
}
}
visitObjectPropertiesWithComputedNames(absVal: AbstractValue): void {
if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return;
@ -289,11 +303,19 @@ class ObjectValueLeakingVisitor {
}
}
visitDescriptor(desc: Descriptor): void {
invariant(desc.value === undefined || desc.value instanceof Value);
if (desc.value !== undefined) this.visitValue(desc.value);
if (desc.get !== undefined) this.visitValue(desc.get);
if (desc.set !== undefined) this.visitValue(desc.set);
visitDescriptor(desc: void | Descriptor): void {
if (desc === undefined) {
} else if (desc instanceof PropertyDescriptor) {
if (desc.value !== undefined) this.visitValue(desc.value);
if (desc.get !== undefined) this.visitValue(desc.get);
if (desc.set !== undefined) this.visitValue(desc.set);
} else if (desc instanceof AbstractJoinedDescriptor) {
this.visitValue(desc.joinCondition);
if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1);
if (desc.descriptor2 !== undefined) this.visitDescriptor(desc.descriptor2);
} else {
invariant(false, "unknown descriptor");
}
}
visitDeclarativeEnvironmentRecordBinding(

View File

@ -16,6 +16,7 @@ import { Completion, ThrowCompletion } from "../completions.js";
import { ObjectValue, StringValue, Value } from "../values/index.js";
import { To } from "../singletons.js";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export class Logger {
constructor(realm: Realm, internalDebug: boolean) {
@ -94,7 +95,10 @@ export class Logger {
} catch (err) {
let message = object.properties.get("message");
console.error(
message && message.descriptor && message.descriptor.value instanceof StringValue
message &&
message.descriptor &&
message.descriptor instanceof PropertyDescriptor &&
message.descriptor.value instanceof StringValue
? message.descriptor.value.value
: "(no message available)"
);

View File

@ -37,6 +37,7 @@ import type {
import invariant from "../invariant.js";
import { Logger } from "./logger.js";
import { SerializerStatistics } from "../serializer/statistics.js";
import { PropertyDescriptor } from "../descriptors.js";
function downgradeErrorsToWarnings(realm: Realm, f: () => any) {
let savedHandler = realm.errorHandler;
@ -238,9 +239,11 @@ export class Modules {
for (let moduleId of globalInitializedModulesMap.properties.keys()) {
let property = globalInitializedModulesMap.properties.get(moduleId);
invariant(property);
let moduleValue = property.descriptor && property.descriptor.value;
if (moduleValue instanceof Value && !moduleValue.mightHaveBeenDeleted()) {
this.initializedModules.set(moduleId, moduleValue);
if (property.descriptor instanceof PropertyDescriptor) {
let moduleValue = property.descriptor && property.descriptor.value;
if (moduleValue instanceof Value && !moduleValue.mightHaveBeenDeleted()) {
this.initializedModules.set(moduleId, moduleValue);
}
}
}
this.getStatistics().initializedModules = this.initializedModules.size;

View File

@ -13,6 +13,7 @@ import type { Realm } from "../realm.js";
import { FatalError } from "../errors.js";
import { Value, StringValue, NumberValue, ObjectValue } from "../values/index.js";
import { Create } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
export default function convert(realm: Realm, val: any): Value {
if (typeof val === "number") {
@ -33,12 +34,15 @@ export default function convert(realm: Realm, val: any): Value {
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
for (let key in val) {
obj.$DefineOwnProperty(key, {
enumerable: true,
writable: true,
configurable: true,
value: convert(realm, val[key]),
});
obj.$DefineOwnProperty(
key,
new PropertyDescriptor({
enumerable: true,
writable: true,
configurable: true,
value: convert(realm, val[key]),
})
);
}
return obj;

View File

@ -79,6 +79,10 @@ export class PathConditionsImplementation extends PathConditions {
return this._assumedConditions.size;
}
getAssumedConditions(): Set<AbstractValue> {
return this._assumedConditions;
}
refineBaseConditons(realm: Realm): void {
if (realm.abstractValueImpliesMax > 0) return;
let refine = (condition: AbstractValue) => {

View File

@ -25,12 +25,13 @@ import {
Value,
} from "./index.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import { IsDataDescriptor, cloneDescriptor, equalDescriptors } from "../methods/index.js";
import { IsDataDescriptor } from "../methods/index.js";
import { Leak, Join, Widen } from "../singletons.js";
import invariant from "../invariant.js";
import { createOperationDescriptor, type OperationDescriptor } from "../utils/generator.js";
import { construct_empty_effects } from "../realm.js";
import { SimpleNormalCompletion } from "../completions.js";
import { cloneDescriptor, equalDescriptors, PropertyDescriptor } from "../descriptors.js";
export default class AbstractObjectValue extends AbstractValue {
constructor(
@ -326,11 +327,17 @@ export default class AbstractObjectValue extends AbstractValue {
invariant(ob2 instanceof ObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
if (d1 === undefined || d2 === undefined || !equalDescriptors(d1, d2)) {
if (d1 === undefined || d2 === undefined) {
// We do not handle the case where different loop iterations result in different kinds of propperties
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
d1 = d1.throwIfNotConcrete(this.$Realm);
d2 = d2.throwIfNotConcrete(this.$Realm);
if (!equalDescriptors(d1, d2)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
let desc = cloneDescriptor(d1);
invariant(desc !== undefined);
if (IsDataDescriptor(this.$Realm, desc)) {
@ -340,7 +347,9 @@ export default class AbstractObjectValue extends AbstractValue {
invariant(d1Value instanceof Value);
let d2Value = d2.value;
invariant(d2Value instanceof Value);
desc.value = Widen.widenValues(this.$Realm, d1Value, d2Value);
let dValue = Widen.widenValues(this.$Realm, d1Value, d2Value);
invariant(dValue instanceof Value);
desc.value = dValue;
} else {
// In this case equalDescriptors guarantees exact equality betwee d1 and d2.
// Inlining the accessors will eventually bring in data properties if the accessors have loop variant behavior
@ -366,7 +375,7 @@ export default class AbstractObjectValue extends AbstractValue {
}
// ECMA262 9.1.6
$DefineOwnProperty(_P: PropertyKeyValue, Desc: Descriptor): boolean {
$DefineOwnProperty(_P: PropertyKeyValue, _Desc: Descriptor): boolean {
let P = _P;
if (P instanceof StringValue) P = P.value;
if (this.values.isTop()) {
@ -378,10 +387,11 @@ export default class AbstractObjectValue extends AbstractValue {
if (elements.size === 1) {
for (let cv of elements) {
invariant(cv instanceof ObjectValue);
return cv.$DefineOwnProperty(P, Desc);
return cv.$DefineOwnProperty(P, _Desc);
}
invariant(false);
} else {
let Desc = _Desc.throwIfNotConcrete(this.$Realm);
if (!IsDataDescriptor(this.$Realm, Desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
@ -395,13 +405,21 @@ export default class AbstractObjectValue extends AbstractValue {
break;
}
}
let desc = {
value: "value" in Desc ? Desc.value : this.$Realm.intrinsics.undefined,
writable: "writable" in Desc ? Desc.writable : firstExistingDesc ? firstExistingDesc.writable : false,
enumerable: "enumerable" in Desc ? Desc.enumerable : firstExistingDesc ? firstExistingDesc.enumerable : false,
if (firstExistingDesc) {
firstExistingDesc = firstExistingDesc.throwIfNotConcrete(this.$Realm);
}
let desc = new PropertyDescriptor({
value: Desc.value !== undefined ? Desc.value : this.$Realm.intrinsics.undefined,
writable: Desc.writable !== undefined ? Desc.writable : firstExistingDesc ? firstExistingDesc.writable : false,
enumerable:
Desc.enumerable !== undefined ? Desc.enumerable : firstExistingDesc ? firstExistingDesc.enumerable : false,
configurable:
"configurable" in Desc ? Desc.configurable : firstExistingDesc ? firstExistingDesc.configurable : false,
};
Desc.configurable !== undefined
? Desc.configurable
: firstExistingDesc
? firstExistingDesc.configurable
: false,
});
let newVal = desc.value;
if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects
@ -412,9 +430,19 @@ export default class AbstractObjectValue extends AbstractValue {
invariant(ob2 instanceof ObjectValue || ob2 instanceof AbstractObjectValue);
let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P);
if ((d1 !== undefined && !equalDescriptors(d1, desc)) || (d2 !== undefined && !equalDescriptors(d2, desc))) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
if (d1 !== undefined) {
d1 = d1.throwIfNotConcrete(this.$Realm);
if (!equalDescriptors(d1, desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
}
if (d2 !== undefined) {
d2 = d2.throwIfNotConcrete(this.$Realm);
if (!equalDescriptors(d2, desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
}
let oldVal1 = d1 === undefined || d1.value === undefined ? this.$Realm.intrinsics.empty : d1.value;
let oldVal2 = d2 === undefined || d2.value === undefined ? this.$Realm.intrinsics.empty : d2.value;
@ -438,9 +466,12 @@ export default class AbstractObjectValue extends AbstractValue {
for (let cv of elements) {
invariant(cv instanceof ObjectValue);
let d = cv.$GetOwnProperty(P);
if (d !== undefined && !equalDescriptors(d, desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
if (d !== undefined) {
d = d.throwIfNotConcrete(this.$Realm);
if (!equalDescriptors(d, desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
}
let dval = d === undefined || d.value === undefined ? this.$Realm.intrinsics.empty : d.value;
invariant(dval instanceof Value);
@ -957,14 +988,18 @@ export default class AbstractObjectValue extends AbstractValue {
let result1 = true;
let result2 = true;
if (d1 !== undefined) {
d1 = d1.throwIfNotConcrete(this.$Realm);
let newDesc1 = cloneDescriptor(d1);
invariant(newDesc1);
newDesc1 = newDesc1.throwIfNotConcrete(this.$Realm);
newDesc1.value = newVal1;
result1 = ob1.$DefineOwnProperty(P, newDesc1);
}
if (d2 !== undefined) {
d2 = d2.throwIfNotConcrete(this.$Realm);
let newDesc2 = cloneDescriptor(d2);
invariant(newDesc2);
newDesc2 = newDesc2.throwIfNotConcrete(this.$Realm);
newDesc2.value = newVal2;
result2 = ob2.$DefineOwnProperty(P, newDesc2);
}

View File

@ -18,6 +18,7 @@ import { SameValuePartial } from "../methods/abstract.js";
import { Get, OrdinaryGet } from "../methods/get.js";
import { Properties } from "../singletons.js";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default class ArgumentsExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {
@ -36,7 +37,8 @@ export default class ArgumentsExotic extends ObjectValue {
// 3. If desc is undefined, return desc.
if (desc === undefined) return undefined;
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
Properties.ThrowIfMightHaveBeenDeleted(desc);
desc = desc.throwIfNotConcrete(this.$Realm);
// 4. Let map be args.[[ParameterMap]].
let map = args.$ParameterMap;
@ -56,7 +58,9 @@ export default class ArgumentsExotic extends ObjectValue {
}
// ECMA262 9.4.4.2
$DefineOwnProperty(P: PropertyKeyValue, Desc: Descriptor): boolean {
$DefineOwnProperty(P: PropertyKeyValue, _Desc: Descriptor): boolean {
let Desc = _Desc.throwIfNotConcrete(this.$Realm);
// 1. Let args be the arguments object.
let args = this;
@ -75,7 +79,7 @@ export default class ArgumentsExotic extends ObjectValue {
// a. If Desc.[[Value]] is not present and Desc.[[Writable]] is present and its value is false, then
if (Desc.value === undefined && Desc.writable === false) {
// i. Let newArgDesc be a copy of Desc.
newArgDesc = Object.assign({}, Desc);
newArgDesc = new PropertyDescriptor(Desc);
// ii. Set newArgDesc.[[Value]] to Get(map, P).
newArgDesc.value = Get(this.$Realm, map, P);

View File

@ -25,6 +25,7 @@ import { Leak, Properties, To, Utils } from "../singletons.js";
import { type OperationDescriptor } from "../utils/generator.js";
import invariant from "../invariant.js";
import { NestedOptimizedFunctionSideEffect } from "../errors.js";
import { PropertyDescriptor } from "../descriptors.js";
type PossibleNestedOptimizedFunctions = [
{ func: BoundFunctionValue | ECMAScriptSourceFunctionValue, thisValue: Value },
@ -98,9 +99,9 @@ function createArrayWithWidenedNumericProperty(
// Add unknownProperty so we manually handle this object property access
abstractArrayValue.unknownProperty = {
key: undefined,
descriptor: {
descriptor: new PropertyDescriptor({
value: AbstractValue.createFromType(realm, Value, "widened numeric property"),
},
}),
object: abstractArrayValue,
};
return abstractArrayValue;
@ -149,7 +150,8 @@ export default class ArrayValue extends ObjectValue {
oldLenDesc !== undefined && !IsAccessorDescriptor(this.$Realm, oldLenDesc),
"cannot be undefined or an accessor descriptor"
);
Properties.ThrowIfMightHaveBeenDeleted(oldLenDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(oldLenDesc);
oldLenDesc = oldLenDesc.throwIfNotConcrete(this.$Realm);
// c. Let oldLen be oldLenDesc.[[Value]].
let oldLen = oldLenDesc.value;
@ -211,8 +213,7 @@ export default class ArrayValue extends ObjectValue {
if (obj instanceof ArrayValue && obj.intrinsicName) {
const prop = obj.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined) {
const desc = prop.descriptor;
const desc = prop.descriptor.throwIfNotConcrete(obj.$Realm);
return desc.value instanceof AbstractValue && desc.value.kind === "widened numeric property";
}
}

View File

@ -45,7 +45,7 @@ export default class FunctionValue extends ObjectValue {
invariant(binding);
let desc = binding.descriptor;
invariant(desc);
let value = desc.value;
let value = desc.throwIfNotConcrete(this.$Realm).value;
if (!(value instanceof NumberValue)) return undefined;
return value.value;
}

View File

@ -18,6 +18,7 @@ import { OrdinaryHasProperty } from "../methods/has.js";
import { IntegerIndexedElementSet, IntegerIndexedElementGet } from "../methods/typedarray.js";
import { Properties, To } from "../singletons.js";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default class IntegerIndexedExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {
@ -51,12 +52,12 @@ export default class IntegerIndexedExotic extends ObjectValue {
if (value instanceof UndefinedValue) return undefined;
// iii. Return a PropertyDescriptor{[[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false}.
return {
return new PropertyDescriptor({
value: value,
writable: true,
enumerable: true,
configurable: false,
};
});
}
}
// 4. Return OrdinaryGetOwnProperty(O, P).
@ -118,7 +119,7 @@ export default class IntegerIndexedExotic extends ObjectValue {
}
// ECMA262 9.4.5.3
$DefineOwnProperty(P: PropertyKeyValue, Desc: Descriptor): boolean {
$DefineOwnProperty(P: PropertyKeyValue, _Desc: Descriptor): boolean {
let O = this;
// 1. Assert: IsPropertyKey(P) is true.
@ -153,6 +154,8 @@ export default class IntegerIndexedExotic extends ObjectValue {
// v. If numericIndex ≥ length, return false.
if (numericIndex >= length) return false;
let Desc = _Desc.throwIfNotConcrete(this.$Realm);
// vi. If IsAccessorDescriptor(Desc) is true, return false.
if (IsAccessorDescriptor(this.$Realm, Desc) === true) return false;
@ -181,7 +184,7 @@ export default class IntegerIndexedExotic extends ObjectValue {
}
// 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
return Properties.OrdinaryDefineOwnProperty(this.$Realm, O, P, Desc);
return Properties.OrdinaryDefineOwnProperty(this.$Realm, O, P, _Desc);
}
// ECMA262 9.4.5.4

View File

@ -25,6 +25,7 @@ import {
import type { PromiseCapability } from "../types.js";
import { ReturnCompletion } from "../completions.js";
import { Functions } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
export type NativeFunctionCallback = (
context: UndefinedValue | NullValue | ObjectValue | AbstractObjectValue,
args: Array<Value>,
@ -64,12 +65,15 @@ export default class NativeFunctionValue extends ECMAScriptFunctionValue {
this.callback = callback;
this.length = length;
this.$DefineOwnProperty("length", {
value: new NumberValue(realm, length),
writable: false,
configurable: true,
enumerable: false,
});
this.$DefineOwnProperty(
"length",
new PropertyDescriptor({
value: new NumberValue(realm, length),
writable: false,
configurable: true,
enumerable: false,
})
);
if (name !== undefined && name !== "") {
if (name instanceof SymbolValue) {
@ -77,12 +81,15 @@ export default class NativeFunctionValue extends ECMAScriptFunctionValue {
} else {
this.name = name;
}
this.$DefineOwnProperty("name", {
value: new StringValue(realm, this.name),
writable: false,
configurable: true,
enumerable: false,
});
this.$DefineOwnProperty(
"name",
new PropertyDescriptor({
value: new StringValue(realm, this.name),
writable: false,
configurable: true,
enumerable: false,
})
);
} else {
this.name = "native";
}

View File

@ -14,7 +14,6 @@ import { ValuesDomain } from "../domains/index.js";
import { FatalError } from "../errors.js";
import type {
DataBlock,
Descriptor,
IterationKind,
ObjectKind,
PromiseReaction,
@ -51,6 +50,7 @@ import { Properties } from "../singletons.js";
import invariant from "../invariant.js";
import type { typeAnnotation } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
import { Descriptor, PropertyDescriptor, type DescriptorInitializer, InternalSlotDescriptor } from "../descriptors.js";
export default class ObjectValue extends ConcreteValue {
constructor(
@ -126,6 +126,7 @@ export default class ObjectValue extends ConcreteValue {
configurable: true,
get: function() {
let binding = this[propBindingName];
invariant(binding === undefined || binding.descriptor instanceof InternalSlotDescriptor);
return binding === undefined ? undefined : binding.descriptor.value;
},
set: function(v) {
@ -150,7 +151,7 @@ export default class ObjectValue extends ConcreteValue {
);
let binding = this[propBindingName];
if (binding === undefined) {
let desc = { writeable: true, value: undefined };
let desc = new InternalSlotDescriptor(undefined);
this[propBindingName] = binding = {
descriptor: desc,
object: this,
@ -459,7 +460,7 @@ export default class ObjectValue extends ConcreteValue {
name: SymbolValue | string,
length: number,
callback: NativeFunctionCallback,
desc?: Descriptor = {}
desc?: DescriptorInitializer
): Value {
let intrinsicName;
if (typeof name === "string") {
@ -474,18 +475,21 @@ export default class ObjectValue extends ConcreteValue {
return fnValue;
}
defineNativeProperty(name: SymbolValue | string, value?: Value | Array<Value>, desc?: Descriptor = {}): void {
defineNativeProperty(name: SymbolValue | string, value?: Value | Array<Value>, desc?: DescriptorInitializer): void {
invariant(!value || value instanceof Value);
this.$DefineOwnProperty(name, {
value,
writable: true,
enumerable: false,
configurable: true,
...desc,
});
this.$DefineOwnProperty(
name,
new PropertyDescriptor({
value,
writable: true,
enumerable: false,
configurable: true,
...desc,
})
);
}
defineNativeGetter(name: SymbolValue | string, callback: NativeFunctionCallback, desc?: Descriptor = {}): void {
defineNativeGetter(name: SymbolValue | string, callback: NativeFunctionCallback, desc?: DescriptorInitializer): void {
let intrinsicName, funcName;
if (typeof name === "string") {
funcName = `get ${name}`;
@ -501,24 +505,30 @@ export default class ObjectValue extends ConcreteValue {
}
let func = new NativeFunctionValue(this.$Realm, intrinsicName, funcName, 0, callback);
this.$DefineOwnProperty(name, {
get: func,
set: this.$Realm.intrinsics.undefined,
enumerable: false,
configurable: true,
...desc,
});
this.$DefineOwnProperty(
name,
new PropertyDescriptor({
get: func,
set: this.$Realm.intrinsics.undefined,
enumerable: false,
configurable: true,
...desc,
})
);
}
defineNativeConstant(name: SymbolValue | string, value?: Value | Array<Value>, desc?: Descriptor = {}): void {
defineNativeConstant(name: SymbolValue | string, value?: Value | Array<Value>, desc?: DescriptorInitializer): void {
invariant(!value || value instanceof Value);
this.$DefineOwnProperty(name, {
value,
writable: false,
enumerable: false,
configurable: false,
...desc,
});
this.$DefineOwnProperty(
name,
new PropertyDescriptor({
value,
writable: false,
enumerable: false,
configurable: false,
...desc,
})
);
}
// Note that internal properties will not be copied to the snapshot, nor will they be removed.
@ -563,8 +573,8 @@ export default class ObjectValue extends ConcreteValue {
let desc = from.$GetOwnProperty(nextKey);
// ii. If desc is not undefined and desc.[[Enumerable]] is true, then
if (desc && desc.enumerable) {
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc && desc.throwIfNotConcrete(this.$Realm).enumerable) {
Properties.ThrowIfMightHaveBeenDeleted(desc);
// 1. Let propValue be ? Get(from, nextKey).
let propValue = Get(this.$Realm, from, nextKey);
@ -581,7 +591,8 @@ export default class ObjectValue extends ConcreteValue {
for (let [key, propertyBinding] of this.properties) {
let desc = propertyBinding.descriptor;
if (desc === undefined) continue; // deleted
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
Properties.ThrowIfMightHaveBeenDeleted(desc);
desc = desc.throwIfNotConcrete(this.$Realm);
let serializedDesc: any = { enumerable: desc.enumerable, configurable: desc.configurable };
if (desc.value) {
serializedDesc.writable = desc.writable;
@ -661,7 +672,11 @@ export default class ObjectValue extends ConcreteValue {
try {
this.$Realm.invariantLevel = 0;
let desc = this.$GetOwnProperty(P);
return desc !== undefined && desc.value instanceof Value ? desc.value : this.$Realm.intrinsics.undefined;
if (desc === undefined) {
return this.$Realm.intrinsics.undefined;
}
desc = desc.throwIfNotConcrete(this.$Realm);
return desc.value ? desc.value : this.$Realm.intrinsics.undefined;
} finally {
this.$Realm.invariantLevel = savedInvariantLevel;
}

View File

@ -304,7 +304,8 @@ export default class ProxyValue extends ObjectValue {
if (trapResultObj instanceof UndefinedValue) {
// a. If targetDesc is undefined, return undefined.
if (!targetDesc) return undefined;
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
targetDesc = targetDesc.throwIfNotConcrete(realm);
// b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if (!targetDesc.configurable) {
@ -344,9 +345,10 @@ export default class ProxyValue extends ObjectValue {
}
// 17. If resultDesc.[[Configurable]] is false, then
resultDesc = resultDesc.throwIfNotConcrete(realm);
if (!resultDesc.configurable) {
// a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
if (!targetDesc || targetDesc.configurable) {
if (!targetDesc || targetDesc.throwIfNotConcrete(realm).configurable) {
// i. Throw a TypeError exception.
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
@ -407,7 +409,7 @@ export default class ProxyValue extends ObjectValue {
// 13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
let settingConfigFalse;
if ("configurable" in Desc && !Desc.configurable) {
if (Desc.throwIfNotConcrete(realm).configurable === false) {
// a. Let settingConfigFalse be true.
settingConfigFalse = true;
} else {
@ -428,7 +430,7 @@ export default class ProxyValue extends ObjectValue {
}
} else {
// 16. Else targetDesc is not undefined,
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
// a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
if (!Properties.IsCompatiblePropertyDescriptor(realm, extensibleTarget, Desc, targetDesc)) {
@ -436,7 +438,7 @@ export default class ProxyValue extends ObjectValue {
}
// b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
if (settingConfigFalse && targetDesc.configurable) {
if (settingConfigFalse && targetDesc.throwIfNotConcrete(realm).configurable) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
}
}
@ -489,7 +491,8 @@ export default class ProxyValue extends ObjectValue {
// b. If targetDesc is not undefined, then
if (targetDesc) {
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
targetDesc = targetDesc.throwIfNotConcrete(realm);
// i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if (!targetDesc.configurable) {
@ -553,7 +556,7 @@ export default class ProxyValue extends ObjectValue {
// 10. If targetDesc is not undefined, then
if (targetDesc) {
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
// a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
if (IsDataDescriptor(realm, targetDesc) && targetDesc.configurable === false && targetDesc.writable === false) {
@ -628,7 +631,7 @@ export default class ProxyValue extends ObjectValue {
// 11. If targetDesc is not undefined, then
if (targetDesc) {
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
// a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Configurable]] is false and targetDesc.[[Writable]] is false, then
if (IsDataDescriptor(realm, targetDesc) && !targetDesc.configurable && !targetDesc.writable) {
@ -699,7 +702,8 @@ export default class ProxyValue extends ObjectValue {
// 11. If targetDesc is undefined, return true.
if (!targetDesc) return true;
Properties.ThrowIfMightHaveBeenDeleted(targetDesc.value);
Properties.ThrowIfMightHaveBeenDeleted(targetDesc);
targetDesc = targetDesc.throwIfNotConcrete(realm);
// 12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
if (!targetDesc.configurable) {
@ -768,10 +772,10 @@ export default class ProxyValue extends ObjectValue {
for (let key of targetKeys) {
// a. Let desc be ? target.[[GetOwnProperty]](key).
let desc = target.$GetOwnProperty(key);
if (desc) Properties.ThrowIfMightHaveBeenDeleted(desc.value);
if (desc) Properties.ThrowIfMightHaveBeenDeleted(desc);
// b. If desc is not undefined and desc.[[Configurable]] is false, then
if (desc && desc.configurable === false) {
if (desc && desc.throwIfNotConcrete(realm).configurable === false) {
// i. Append key as an element of targetNonconfigurableKeys.
targetNonconfigurableKeys.push(key);
} else {

View File

@ -15,6 +15,7 @@ import { ObjectValue, NumberValue, StringValue } from "./index.js";
import { IsInteger, IsArrayIndex } from "../methods/is.js";
import { Properties, To } from "../singletons.js";
import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default class StringExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {
@ -30,7 +31,7 @@ export default class StringExotic extends ObjectValue {
// 3. If desc is not undefined, return desc.
if (desc !== undefined) {
Properties.ThrowIfMightHaveBeenDeleted(desc.value);
Properties.ThrowIfMightHaveBeenDeleted(desc);
return desc;
}
@ -67,12 +68,12 @@ export default class StringExotic extends ObjectValue {
let resultStr = new StringValue(this.$Realm, str.value.charAt(index));
// 13. Return a PropertyDescriptor{[[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}.
return {
return new PropertyDescriptor({
value: resultStr,
writable: false,
enumerable: true,
configurable: false,
};
});
}
// ECMA262 9.4.3.2

View File

@ -42,7 +42,6 @@ export default class StringValue extends PrimitiveValue {
}
toDisplayString(): string {
// TODO: proper escaping
return `"${this.value}"`;
return JSON.stringify(this.value);
}
}

View File

@ -1,3 +1,4 @@
// throws introspection error
let x = global.__abstract ? __abstract("boolean", "true") : true;
let ob1 = {};
Object.defineProperty(ob1, "p", { writable: false, value: 1 });