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-react
yarn test-sourcemaps yarn test-sourcemaps
yarn test-std-in 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 #yarn test-test262-new --statusFile ~/artifacts/test262-new-status.txt --timeout 120 --verbose
- store_artifacts: - store_artifacts:
path: ~/artifacts/ path: ~/artifacts/

View File

@ -47,6 +47,7 @@ let execSpec;
let JSONTokenizer = require("../lib/utils/JSONTokenizer.js").default; let JSONTokenizer = require("../lib/utils/JSONTokenizer.js").default;
let { Linter } = require("eslint"); let { Linter } = require("eslint");
let lintConfig = require("./lint-config"); let lintConfig = require("./lint-config");
let TextPrinter = require("../lib/utils/TextPrinter.js").TextPrinter;
function transformWithBabel(code, plugins, presets) { function transformWithBabel(code, plugins, presets) {
return babel.transform(code, { return babel.transform(code, {
@ -431,6 +432,7 @@ function runTest(name, code, options: PrepackOptions, args) {
return Promise.resolve(false); return Promise.resolve(false);
} else { } else {
let codeIterations = []; let codeIterations = [];
let irIterations = [];
let markersToFind = []; let markersToFind = [];
for (let [positive, marker] of [[true, "// does contain:"], [false, "// does not contain:"]]) { 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)) { 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); 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); let serialized = prepackSources([{ filePath: name, fileContents: code, sourceMapContents: "" }], options);
if (serialized.statistics && serialized.statistics.delayedValues > 0) anyDelayedValues = true; if (serialized.statistics && serialized.statistics.delayedValues > 0) anyDelayedValues = true;
if (!serialized) { if (!serialized) {
@ -583,7 +593,11 @@ function runTest(name, code, options: PrepackOptions, args) {
if (!execSpec && options.lazyObjectsRuntime !== undefined) { if (!execSpec && options.lazyObjectsRuntime !== undefined) {
codeToRun = augmentCodeWithLazyObjectSupport(codeToRun, args.lazyObjectsRuntime); 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)); codeIterations.push(unescapeUniqueSuffix(codeToRun, options.uniqueSuffix));
if (args.es5) { if (args.es5) {
codeToRun = transformWithBabel( 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(chalk.underline("output of inspect() on original code"));
console.error(expected); console.error(expected);
for (let ii = 0; ii < codeIterations.length; ii++) { 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(chalk.underline(`generated code in iteration ${ii}`));
console.error(codeIterations[ii]); console.error(codeIterations[ii]);
} }
@ -829,6 +847,7 @@ class ProgramArgs {
noLazySupport: boolean; noLazySupport: boolean;
fast: boolean; fast: boolean;
cpuprofilePath: string; cpuprofilePath: string;
ir: boolean;
constructor( constructor(
debugNames: boolean, debugNames: boolean,
debugScopes: boolean, debugScopes: boolean,
@ -839,7 +858,8 @@ class ProgramArgs {
lazyObjectsRuntime: string, lazyObjectsRuntime: string,
noLazySupport: boolean, noLazySupport: boolean,
fast: boolean, fast: boolean,
cpuProfilePath: string cpuProfilePath: string,
ir: boolean
) { ) {
this.debugNames = debugNames; this.debugNames = debugNames;
this.debugScopes = debugScopes; this.debugScopes = debugScopes;
@ -851,6 +871,7 @@ class ProgramArgs {
this.noLazySupport = noLazySupport; this.noLazySupport = noLazySupport;
this.fast = fast; this.fast = fast;
this.cpuprofilePath = cpuProfilePath; this.cpuprofilePath = cpuProfilePath;
this.ir = ir;
} }
} }
@ -885,7 +906,7 @@ function usage(): string {
return ( return (
`Usage: ${process.argv[0]} ${process.argv[1]} ` + `Usage: ${process.argv[0]} ${process.argv[1]} ` +
EOL + 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. // to run tests. If not a separate node context used.
lazyObjectsRuntime: LAZY_OBJECTS_RUNTIME_NAME, lazyObjectsRuntime: LAZY_OBJECTS_RUNTIME_NAME,
noLazySupport: false, noLazySupport: false,
ir: false,
fast: false, fast: false,
cpuprofilePath: "", cpuprofilePath: "",
}, },
@ -949,6 +971,9 @@ function argsParse(): ProgramArgs {
if (typeof parsedArgs.cpuprofilePath !== "string") { if (typeof parsedArgs.cpuprofilePath !== "string") {
throw new ArgsParseError("cpuprofilePath must be a 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( let programArgs = new ProgramArgs(
parsedArgs.debugNames, parsedArgs.debugNames,
parsedArgs.debugScopes, parsedArgs.debugScopes,
@ -959,7 +984,8 @@ function argsParse(): ProgramArgs {
parsedArgs.lazyObjectsRuntime, parsedArgs.lazyObjectsRuntime,
parsedArgs.noLazySupport, parsedArgs.noLazySupport,
parsedArgs.fast, parsedArgs.fast,
parsedArgs.cpuprofilePath parsedArgs.cpuprofilePath,
parsedArgs.ir
); );
return programArgs; 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 { createOperationDescriptor } from "./utils/generator.js";
import { SourceMapConsumer, type NullableMappedPosition } from "source-map"; import { SourceMapConsumer, type NullableMappedPosition } from "source-map";
import { PropertyDescriptor } from "./descriptors.js";
function deriveGetBinding(realm: Realm, binding: Binding) { function deriveGetBinding(realm: Realm, binding: Binding) {
let types = TypesDomain.topVal; 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}). // 4. Return ? DefinePropertyOrThrow(bindings, N, PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: configValue}).
return new BooleanValue( return new BooleanValue(
realm, realm,
Properties.DefinePropertyOrThrow(realm, bindings, N, { Properties.DefinePropertyOrThrow(
value: realm.intrinsics.undefined, realm,
writable: true, bindings,
enumerable: true, N,
configurable: configValue, 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. // 5. If existingProp is undefined, return false.
if (!existingProp) 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. // 6. If existingProp.[[Configurable]] is true, return false.
if (existingProp.configurable) return false; if (existingProp.configurable) return false;
@ -948,7 +955,8 @@ export class GlobalEnvironmentRecord extends EnvironmentRecord {
// 5. If existingProp is undefined, return ? IsExtensible(globalObject). // 5. If existingProp is undefined, return ? IsExtensible(globalObject).
if (!existingProp) return IsExtensible(realm, 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. // 6. If existingProp.[[Configurable]] is true, return true.
if (existingProp.configurable) return true; if (existingProp.configurable) return true;
@ -1015,17 +1023,20 @@ export class GlobalEnvironmentRecord extends EnvironmentRecord {
// 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N). // 4. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
let existingProp = 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 // 5. If existingProp is undefined or existingProp.[[Configurable]] is true, then
let desc; let desc;
if (!existingProp || existingProp.configurable) { if (!existingProp || existingProp.configurable) {
// a. Let desc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D}. // 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 { } else {
// 6. Else, // 6. Else,
Properties.ThrowIfMightHaveBeenDeleted(existingProp.value); Properties.ThrowIfMightHaveBeenDeleted(existingProp);
// a. Let desc be the PropertyDescriptor{[[Value]]: V }. // a. Let desc be the PropertyDescriptor{[[Value]]: V }.
desc = { value: V }; desc = new PropertyDescriptor({ value: V });
} }
// 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc). // 7. Perform ? DefinePropertyOrThrow(globalObject, N, desc).

View File

@ -155,7 +155,7 @@ function emitResidualLoopIfSafe(
if (key.object.unknownProperty === key) { if (key.object.unknownProperty === key) {
targetObject = key.object; targetObject = key.object;
invariant(desc !== undefined); invariant(desc !== undefined);
let sourceValue = desc.value; let sourceValue = desc.throwIfNotConcrete(realm).value;
if (sourceValue instanceof AbstractValue) { if (sourceValue instanceof AbstractValue) {
// because sourceValue was written to key.object.unknownProperty it must be that // because sourceValue was written to key.object.unknownProperty it must be that
let cond = sourceValue.args[0]; 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 { Create, Functions, Properties } from "../singletons.js";
import { StringValue } from "../values/index.js"; import { StringValue } from "../values/index.js";
import IsStrict from "../utils/strict.js"; import IsStrict from "../utils/strict.js";
import { PropertyDescriptor } from "../descriptors.js";
import type { BabelNodeFunctionDeclaration } from "@babel/types"; import type { BabelNodeFunctionDeclaration } from "@babel/types";
// ECMA262 14.1.20 // ECMA262 14.1.20
@ -44,11 +45,16 @@ export default function(
let prototype = Create.ObjectCreate(realm, realm.intrinsics.GeneratorPrototype); let prototype = Create.ObjectCreate(realm, realm.intrinsics.GeneratorPrototype);
// 5. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}). // 5. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, F, "prototype", { Properties.DefinePropertyOrThrow(
value: prototype, realm,
writable: true, F,
configurable: false, "prototype",
}); new PropertyDescriptor({
value: prototype,
writable: true,
configurable: false,
})
);
// 6. Perform SetFunctionName(F, name). // 6. Perform SetFunctionName(F, name).
Functions.SetFunctionName(realm, 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 IsStrict from "../utils/strict.js";
import type { BabelNodeFunctionExpression } from "@babel/types"; import type { BabelNodeFunctionExpression } from "@babel/types";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default function( export default function(
ast: BabelNodeFunctionExpression, ast: BabelNodeFunctionExpression,
@ -57,12 +58,17 @@ export default function(
prototype.originalConstructor = closure; prototype.originalConstructor = closure;
// 9. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}). // 9. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, closure, "prototype", { Properties.DefinePropertyOrThrow(
value: prototype, realm,
writable: true, closure,
enumerable: false, "prototype",
configurable: false, new PropertyDescriptor({
}); value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
// 10. Perform SetFunctionName(closure, name). // 10. Perform SetFunctionName(closure, name).
Functions.SetFunctionName(realm, closure, new StringValue(realm, name)); Functions.SetFunctionName(realm, closure, new StringValue(realm, name));
@ -126,12 +132,17 @@ export default function(
prototype.originalConstructor = closure; prototype.originalConstructor = closure;
// 5. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}). // 5. Perform DefinePropertyOrThrow(closure, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, closure, "prototype", { Properties.DefinePropertyOrThrow(
value: prototype, realm,
writable: true, closure,
enumerable: false, "prototype",
configurable: false, new PropertyDescriptor({
}); value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
// 6. Return closure. // 6. Return closure.
return closure; return closure;

View File

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

View File

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

View File

@ -1749,7 +1749,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
let elem = O.$GetOwnProperty(i.toString()); let elem = O.$GetOwnProperty(i.toString());
// b.If elem is undefined, return true. // b.If elem is undefined, return true.
if (elem === undefined) return true; if (elem === undefined) return true;
Properties.ThrowIfMightHaveBeenDeleted(elem.value); Properties.ThrowIfMightHaveBeenDeleted(elem);
} }
// 2.Return false. // 2.Return false.
return false; return false;
@ -1782,8 +1782,8 @@ export default function(realm: Realm, obj: ObjectValue): void {
for (let j = 0; j < len; j++) { for (let j = 0; j < len; j++) {
// is a data property whose [[Configurable]] attribute is false. // is a data property whose [[Configurable]] attribute is false.
let prop = O.$GetOwnProperty(j.toString()); let prop = O.$GetOwnProperty(j.toString());
if (prop !== undefined && !prop.configurable) { if (prop !== undefined && !prop.throwIfNotConcrete(realm).configurable) {
Properties.ThrowIfMightHaveBeenDeleted(prop.value); Properties.ThrowIfMightHaveBeenDeleted(prop);
throw Error( throw Error(
"Implementation defined behavior : Array is sparse and it's prototype has some numbered properties" "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++) { for (let j = 0; j < len; j++) {
//is a data property whose [[writable]] attribute is false. //is a data property whose [[writable]] attribute is false.
let prop = O.$GetOwnProperty(j.toString()); let prop = O.$GetOwnProperty(j.toString());
if (prop !== undefined && !prop.writable) { if (prop !== undefined && !prop.throwIfNotConcrete(realm).writable) {
Properties.ThrowIfMightHaveBeenDeleted(prop.value); Properties.ThrowIfMightHaveBeenDeleted(prop);
throw Error("Implementation defined behavior : property " + j.toString() + "is non writable : "); 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 { Create, Properties, To } from "../../singletons.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import type { BabelNodeSourceLocation } from "@babel/types"; import type { BabelNodeSourceLocation } from "@babel/types";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm): NativeFunctionValue { export default function(realm: Realm): NativeFunctionValue {
return build("Error", realm, false); 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); let msg = message.getType() === StringValue ? message : To.ToStringValue(realm, message);
// b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}. // b. Let msgDesc be the PropertyDescriptor{[[Value]]: msg, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let msgDesc = { let msgDesc = new PropertyDescriptor({
value: msg, value: msg,
writable: true, writable: true,
enumerable: false, enumerable: false,
configurable: true, configurable: true,
}; });
// c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc). // c. Perform ! DefinePropertyOrThrow(O, "message", msgDesc).
Properties.DefinePropertyOrThrow(realm, 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. // Build a text description of the stack.
let stackDesc = { let stackDesc = new PropertyDescriptor({
value: buildStack(realm, O), value: buildStack(realm, O),
enumerable: false, enumerable: false,
configurable: true, configurable: true,
writable: true, writable: true,
}; });
Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc); Properties.DefinePropertyOrThrow(realm, O, "stack", stackDesc);
// 4. Return O. // 4. Return O.

View File

@ -29,6 +29,7 @@ import { IsCallable } from "../../methods/is.js";
import { HasOwnProperty, HasSomeCompatibleType } from "../../methods/has.js"; import { HasOwnProperty, HasSomeCompatibleType } from "../../methods/has.js";
import { OrdinaryHasInstance } from "../../methods/abstract.js"; import { OrdinaryHasInstance } from "../../methods/abstract.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void { export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 19.2.3 // 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}). // 8. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, F, "length", { Properties.DefinePropertyOrThrow(
value: new NumberValue(realm, L), realm,
writable: false, F,
enumerable: false, "length",
configurable: true, new PropertyDescriptor({
}); value: new NumberValue(realm, L),
writable: false,
enumerable: false,
configurable: true,
})
);
// 9. Let targetName be ? Get(Target, "name"). // 9. Let targetName be ? Get(Target, "name").
let targetName = Get(realm, Target, new StringValue(realm, "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); let clone = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
for (let [key, binding] of val.properties) { for (let [key, binding] of val.properties) {
if (binding === undefined || binding.descriptor === undefined) continue; // deleted if (binding === undefined || binding.descriptor === undefined) continue; // deleted
invariant(binding.descriptor !== undefined); let desc = binding.descriptor;
let value = binding.descriptor.value; invariant(desc !== undefined);
Properties.ThrowIfMightHaveBeenDeleted(value); Properties.ThrowIfMightHaveBeenDeleted(desc);
let value = desc.throwIfNotConcrete(realm).value;
if (value === undefined) { if (value === undefined) {
AbstractValue.reportIntrospectionError(val, key); // cannot handle accessors AbstractValue.reportIntrospectionError(val, key); // cannot handle accessors
throw new FatalError(); 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 { Call, CreateMapIterator, IsCallable, SameValueZeroPartial } from "../../methods/index.js";
import { Properties } from "../../singletons.js"; import { Properties } from "../../singletons.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void { export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.1.3.1 // ECMA262 23.1.3.1
@ -257,39 +258,42 @@ export default function(realm: Realm, obj: ObjectValue): void {
}); });
// ECMA262 23.1.3.10 // ECMA262 23.1.3.10
obj.$DefineOwnProperty("size", { obj.$DefineOwnProperty(
configurable: true, "size",
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => { new PropertyDescriptor({
// 1. Let M be the this value. configurable: true,
let M = context.throwIfNotConcrete(); 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. // 2. If Type(M) is not Object, throw a TypeError exception.
if (!(M instanceof ObjectValue)) { if (!(M instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
} }
// 3. If M does not have a [[MapData]] internal slot, throw a TypeError exception. // 3. If M does not have a [[MapData]] internal slot, throw a TypeError exception.
if (!M.$MapData) { if (!M.$MapData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
} }
// 4. Let entries be the List that is the value of M's [[MapData]] internal slot. // 4. Let entries be the List that is the value of M's [[MapData]] internal slot.
let entries = M.$MapData; let entries = M.$MapData;
invariant(entries !== undefined); invariant(entries !== undefined);
// 5. Let count be 0. // 5. Let count be 0.
let count = 0; let count = 0;
// 6. For each Record {[[Key]], [[Value]]} p that is an element of entries // 6. For each Record {[[Key]], [[Value]]} p that is an element of entries
for (let p of entries) { for (let p of entries) {
// a. If p.[[Key]] is not empty, set count to count+1. // a. If p.[[Key]] is not empty, set count to count+1.
if (p.$Key !== undefined) count++; if (p.$Key !== undefined) count++;
} }
// 7. Return count. // 7. Return count.
return new NumberValue(realm, count); return new NumberValue(realm, count);
}), }),
}); })
);
// ECMA262 23.1.3.11 // ECMA262 23.1.3.11
obj.defineNativeMethod("values", 0, context => { obj.defineNativeMethod("values", 0, context => {
@ -302,9 +306,9 @@ export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.1.3.12 // ECMA262 23.1.3.12
let entriesPropertyDescriptor = obj.$GetOwnProperty("entries"); let entriesPropertyDescriptor = obj.$GetOwnProperty("entries");
invariant(entriesPropertyDescriptor); invariant(entriesPropertyDescriptor instanceof PropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(entriesPropertyDescriptor.value); Properties.ThrowIfMightHaveBeenDeleted(entriesPropertyDescriptor);
obj.defineNativeProperty(realm.intrinsics.SymbolIterator, undefined, entriesPropertyDescriptor); obj.$DefineOwnProperty(realm.intrinsics.SymbolIterator, entriesPropertyDescriptor);
// ECMA262 23.1.3.13 // ECMA262 23.1.3.13
obj.defineNativeProperty(realm.intrinsics.SymbolToStringTag, new StringValue(realm, "Map"), { writable: false }); 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 { Create, Leak, Properties as Props, To } from "../../singletons.js";
import { createOperationDescriptor } from "../../utils/generator.js"; import { createOperationDescriptor } from "../../utils/generator.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
function snapshotToObjectAndRemoveProperties( function snapshotToObjectAndRemoveProperties(
to: ObjectValue | AbstractObjectValue, to: ObjectValue | AbstractObjectValue,
@ -96,8 +97,8 @@ function copyKeys(realm: Realm, keys, from, to): void {
let desc = from.$GetOwnProperty(nextKey); let desc = from.$GetOwnProperty(nextKey);
// ii. If desc is not undefined and desc.[[Enumerable]] is true, then // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
if (desc && desc.enumerable) { if (desc && desc.throwIfNotConcrete(realm).enumerable) {
Props.ThrowIfMightHaveBeenDeleted(desc.value); Props.ThrowIfMightHaveBeenDeleted(desc);
// 1. Let propValue be ? Get(from, nextKey). // 1. Let propValue be ? Get(from, nextKey).
let propValue = Get(realm, 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). // 3. Let desc be ? obj.[[GetOwnProperty]](key).
let desc = obj.$GetOwnProperty(key); let desc = obj.$GetOwnProperty(key);
let getterFunc = desc && desc.get;
// If we are returning a descriptor with a NativeFunctionValue // If we are returning a descriptor with a NativeFunctionValue
// and it has no intrinsic name, then we create a temporal as this // and it has no intrinsic name, then we create a temporal as this
// can only be done at runtime // can only be done at runtime
if ( if (desc instanceof PropertyDescriptor) {
getterFunc instanceof NativeFunctionValue && let getterFunc = desc.get;
getterFunc.intrinsicName === undefined && if (
realm.useAbstractInterpretation getterFunc instanceof NativeFunctionValue &&
) { getterFunc.intrinsicName === undefined &&
invariant(P instanceof Value); realm.useAbstractInterpretation
// this will create a property descriptor at runtime ) {
let result = AbstractValue.createTemporalFromBuildFunction( invariant(P instanceof Value);
realm, // this will create a property descriptor at runtime
ObjectValue, let result = AbstractValue.createTemporalFromBuildFunction(
[getOwnPropertyDescriptor, obj, P], realm,
createOperationDescriptor("OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR") ObjectValue,
); [getOwnPropertyDescriptor, obj, P],
invariant(result instanceof AbstractObjectValue); createOperationDescriptor("OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR")
result.makeSimple(); );
let get = Get(realm, result, "get"); invariant(result instanceof AbstractObjectValue);
let set = Get(realm, result, "set"); result.makeSimple();
invariant(get instanceof AbstractValue); let get = Get(realm, result, "get");
invariant(set instanceof AbstractValue); let set = Get(realm, result, "set");
desc = { invariant(get instanceof AbstractValue);
get, invariant(set instanceof AbstractValue);
set, desc = new PropertyDescriptor({
enumerable: false, get,
configurable: true, set,
}; enumerable: false,
configurable: true,
});
}
} }
// 4. Return FromPropertyDescriptor(desc). // 4. Return FromPropertyDescriptor(desc).
@ -404,7 +407,7 @@ export default function(realm: Realm): NativeFunctionValue {
for (let key of ownKeys) { for (let key of ownKeys) {
// a. Let desc be ? obj.[[GetOwnProperty]](key). // a. Let desc be ? obj.[[GetOwnProperty]](key).
let desc = 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). // b. Let descriptor be ! FromPropertyDescriptor(desc).
let descriptor = Props.FromPropertyDescriptor(realm, desc); let descriptor = Props.FromPropertyDescriptor(realm, desc);

View File

@ -26,6 +26,7 @@ import { FatalError } from "../../errors.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import { TypesDomain, ValuesDomain } from "../../domains/index.js"; import { TypesDomain, ValuesDomain } from "../../domains/index.js";
import { createOperationDescriptor } from "../../utils/generator.js"; import { createOperationDescriptor } from "../../utils/generator.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void { export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 19.1.3.2 // ECMA262 19.1.3.2
@ -100,7 +101,8 @@ export default function(realm: Realm, obj: ObjectValue): void {
// 4. If desc is undefined, return false. // 4. If desc is undefined, return false.
if (!desc) return realm.intrinsics.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]]. // 5. Return the value of desc.[[Enumerable]].
return desc.enumerable === undefined ? realm.intrinsics.undefined : new BooleanValue(realm, 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); return To.ToObject(realm, context);
}); });
obj.$DefineOwnProperty("__proto__", { obj.$DefineOwnProperty(
// B.2.2.1.1 "__proto__",
get: new NativeFunctionValue(realm, undefined, "get __proto__", 0, context => { new PropertyDescriptor({
// 1. Let O be ? ToObject(this value). // B.2.2.1.1
let O = To.ToObject(realm, context); 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]](). // 2. Return ? O.[[GetPrototypeOf]]().
return O.$GetPrototypeOf(); return O.$GetPrototypeOf();
}), }),
// B.2.2.1.2 // B.2.2.1.2
set: new NativeFunctionValue(realm, undefined, "set __proto__", 1, (context, [proto]) => { set: new NativeFunctionValue(realm, undefined, "set __proto__", 1, (context, [proto]) => {
// 1. Let O be ? RequireObjectCoercible(this value). // 1. Let O be ? RequireObjectCoercible(this value).
let O = RequireObjectCoercible(realm, context); let O = RequireObjectCoercible(realm, context);
// 2. If Type(proto) is neither Object nor Null, return undefined. // 2. If Type(proto) is neither Object nor Null, return undefined.
if (!HasSomeCompatibleType(proto, ObjectValue, NullValue)) return realm.intrinsics.undefined; if (!HasSomeCompatibleType(proto, ObjectValue, NullValue)) return realm.intrinsics.undefined;
// 3. If Type(O) is not Object, return undefined. // 3. If Type(O) is not Object, return undefined.
if (!O.mightBeObject()) return realm.intrinsics.undefined; if (!O.mightBeObject()) return realm.intrinsics.undefined;
O = O.throwIfNotConcreteObject(); O = O.throwIfNotConcreteObject();
// 4. Let status be ? O.[[SetPrototypeOf]](proto). // 4. Let status be ? O.[[SetPrototypeOf]](proto).
let status = O.$SetPrototypeOf(((proto.throwIfNotConcrete(): any): ObjectValue | NullValue)); let status = O.$SetPrototypeOf(((proto.throwIfNotConcrete(): any): ObjectValue | NullValue));
// 5. If status is false, throw a TypeError exception. // 5. If status is false, throw a TypeError exception.
if (!status) { if (!status) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "couldn't set proto"); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "couldn't set proto");
} }
// 6. Return undefined. // 6. Return undefined.
return realm.intrinsics.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 { Call, CreateSetIterator, IsCallable, SameValueZeroPartial } from "../../methods/index.js";
import { Properties } from "../../singletons.js"; import { Properties } from "../../singletons.js";
import invariant from "../../invariant.js"; import invariant from "../../invariant.js";
import { PropertyDescriptor } from "../../descriptors.js";
export default function(realm: Realm, obj: ObjectValue): void { export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.2.3.1 // 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 // ECMA262 23.2.3.9 get Set.prototype.size
obj.$DefineOwnProperty("size", { obj.$DefineOwnProperty(
get: new NativeFunctionValue(realm, undefined, "get size", 0, context => { "size",
// 1. Let S be the this value. new PropertyDescriptor({
let S = context.throwIfNotConcrete(); 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. // 2. If Type(S) is not Object, throw a TypeError exception.
if (!(S instanceof ObjectValue)) { if (!(S instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
} }
// 3. If S does not have a [[SetData]] internal slot, throw a TypeError exception. // 3. If S does not have a [[SetData]] internal slot, throw a TypeError exception.
if (!S.$SetData) { if (!S.$SetData) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError);
} }
// 4. Let entries be the List that is the value of S's [[SetData]] internal slot. // 4. Let entries be the List that is the value of S's [[SetData]] internal slot.
let entries = S.$SetData; let entries = S.$SetData;
// 5. Let count be 0. // 5. Let count be 0.
let count = 0; let count = 0;
// 6. For each e that is an element of entries // 6. For each e that is an element of entries
for (let e of entries) { for (let e of entries) {
// a. If e is not empty, set count to count+1. // a. If e is not empty, set count to count+1.
if (e) count++; if (e) count++;
} }
// 7. Return count. // 7. Return count.
return new NumberValue(realm, count); return new NumberValue(realm, count);
}), }),
configurable: true, configurable: true,
}); })
);
// ECMA262 23.2.3.10 // ECMA262 23.2.3.10
obj.defineNativeMethod("values", 0, context => { obj.defineNativeMethod("values", 0, context => {
@ -244,12 +248,12 @@ export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 23.2.3.8 // ECMA262 23.2.3.8
let valuesPropertyDescriptor = obj.$GetOwnProperty("values"); let valuesPropertyDescriptor = obj.$GetOwnProperty("values");
invariant(valuesPropertyDescriptor); invariant(valuesPropertyDescriptor instanceof PropertyDescriptor);
Properties.ThrowIfMightHaveBeenDeleted(valuesPropertyDescriptor.value); Properties.ThrowIfMightHaveBeenDeleted(valuesPropertyDescriptor);
obj.defineNativeProperty("keys", undefined, valuesPropertyDescriptor); obj.$DefineOwnProperty("keys", valuesPropertyDescriptor);
// ECMA262 23.2.3.11 // 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 ] // ECMA262 23.2.3.12 Set.prototype [ @@toStringTag ]
obj.defineNativeProperty(realm.intrinsics.SymbolToStringTag, new StringValue(realm, "Set"), { writable: false }); obj.defineNativeProperty(realm.intrinsics.SymbolToStringTag, new StringValue(realm, "Set"), { writable: false });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,6 +34,7 @@ import { CompilerDiagnostic, FatalError } from "../../errors.js";
import * as t from "@babel/types"; import * as t from "@babel/types";
import { createOperationDescriptor, type OperationDescriptor } from "../../utils/generator.js"; import { createOperationDescriptor, type OperationDescriptor } from "../../utils/generator.js";
import { createAndValidateArgModel } from "../../utils/ShapeInformation"; import { createAndValidateArgModel } from "../../utils/ShapeInformation";
import { PropertyDescriptor } from "../../descriptors.js";
export function createAbstractFunction(realm: Realm, ...additionalValues: Array<ConcreteValue>): NativeFunctionValue { export function createAbstractFunction(realm: Realm, ...additionalValues: Array<ConcreteValue>): NativeFunctionValue {
return new NativeFunctionValue( return new NativeFunctionValue(
@ -61,15 +62,18 @@ export function createAbstractFunction(realm: Realm, ...additionalValues: Array<
export default function(realm: Realm): void { export default function(realm: Realm): void {
let global = realm.$GlobalObject; let global = realm.$GlobalObject;
global.$DefineOwnProperty("__dump", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__dump", "__dump", 0, (context, args) => { "__dump",
console.log("dump", args.map(arg => arg.serialize())); new PropertyDescriptor({
return context; value: new NativeFunctionValue(realm, "global.__dump", "__dump", 0, (context, args) => {
}), console.log("dump", args.map(arg => arg.serialize()));
writable: true, return context;
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// Helper function to model values that are obtained from the environment, // Helper function to model values that are obtained from the environment,
// and whose concrete values are not known at Prepack-time. // 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. // the abstract value's name.
// If the abstract value gets somehow embedded in the final heap, // If the abstract value gets somehow embedded in the final heap,
// it will be referred to by the supplied name in the generated code. // it will be referred to by the supplied name in the generated code.
global.$DefineOwnProperty("__abstract", { global.$DefineOwnProperty(
value: createAbstractFunction(realm), "__abstract",
writable: true, new PropertyDescriptor({
enumerable: false, value: createAbstractFunction(realm),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrNull", { global.$DefineOwnProperty(
value: createAbstractFunction(realm, realm.intrinsics.null), "__abstractOrNull",
writable: true, new PropertyDescriptor({
enumerable: false, value: createAbstractFunction(realm, realm.intrinsics.null),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrNullOrUndefined", { global.$DefineOwnProperty(
value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined), "__abstractOrNullOrUndefined",
writable: true, new PropertyDescriptor({
enumerable: false, value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__abstractOrUndefined", { global.$DefineOwnProperty(
value: createAbstractFunction(realm, realm.intrinsics.undefined), "__abstractOrUndefined",
writable: true, new PropertyDescriptor({
enumerable: false, value: createAbstractFunction(realm, realm.intrinsics.undefined),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// Allows dynamically registering optimized functions. // Allows dynamically registering optimized functions.
// WARNING: these functions will get exposed at global scope and called there. // 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 // that is not subsequently applied, the function will not be registered
// (because prepack won't have a correct value for the FunctionValue itself) // (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 // If we encounter an invalid input, we will emit a warning and not optimize the function
global.$DefineOwnProperty("__optimize", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__optimize", "__optimize", 1, (context, [value, argModelString]) => { "__optimize",
let argModel; new PropertyDescriptor({
if (argModelString !== undefined) { value: new NativeFunctionValue(
argModel = createAndValidateArgModel(realm, argModelString); realm,
} "global.__optimize",
if (value instanceof ECMAScriptSourceFunctionValue || value instanceof AbstractValue) { "__optimize",
let currentArgModel = realm.optimizedFunctions.get(value); 1,
// Verify that if there is an existing argModel, that it is the same as the new one. (context, [value, argModelString]) => {
if (currentArgModel) { let argModel;
let currentString = argModelString instanceof StringValue ? argModelString.value : argModelString; if (argModelString !== undefined) {
if (JSON.stringify(currentArgModel) !== currentString) { argModel = createAndValidateArgModel(realm, argModelString);
let argModelError = new CompilerDiagnostic( }
"__optimize called twice with different argModelStrings", if (value instanceof ECMAScriptSourceFunctionValue || value instanceof AbstractValue) {
realm.currentLocation, let currentArgModel = realm.optimizedFunctions.get(value);
"PP1008", // Verify that if there is an existing argModel, that it is the same as the new one.
"Warning" 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; else return realm.intrinsics.undefined;
} }
return value;
} }
realm.optimizedFunctions.set(value, argModel); ),
} else { writable: true,
let location = value.expressionLocation enumerable: false,
? `${value.expressionLocation.start.line}:${value.expressionLocation.start.column} ` + configurable: true,
`${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,
});
if (realm.react.enabled) { 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( value: new ObjectValue(
realm, realm,
realm.intrinsics.ObjectPrototype, realm.intrinsics.ObjectPrototype,
"__reactComponentTrees", "__initializedModules",
/* refuseSerialization */ true /* refuseSerialization */ true
), ),
writable: true, writable: true,
enumerable: false, enumerable: false,
configurable: true, 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, realm,
"global.__optimizeReactComponentTree", realm.intrinsics.ObjectPrototype,
"__optimizeReactComponentTree", "__checkedBindings",
0, /* refuseSerialization */ true
(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, writable: true,
enumerable: false, enumerable: false,
configurable: true, 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 // Helper function used to instantiate a residual function
function createNativeFunctionForResidualCall(unsafe: boolean): NativeFunctionValue { 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 // Helper function that specifies a dynamic invariant that cannot be evaluated at prepack time, and needs code to
// be injected into the serialized output. // be injected into the serialized output.
global.$DefineOwnProperty("__assume", { global.$DefineOwnProperty(
value: createNativeFunctionForResidualInjection( "__assume",
"__assume", new PropertyDescriptor({
([c, s]): void => { value: createNativeFunctionForResidualInjection(
if (!c.mightBeTrue()) { "__assume",
let error = new CompilerDiagnostic( ([c, s]): void => {
`Assumed condition cannot hold`, if (!c.mightBeTrue()) {
realm.currentLocation, let error = new CompilerDiagnostic(
"PP0040", `Assumed condition cannot hold`,
"FatalError" realm.currentLocation,
); "PP0040",
realm.handleError(error); "FatalError"
throw new FatalError(); );
} realm.handleError(error);
Path.pushAndRefine(c); throw new FatalError();
}, }
createOperationDescriptor("ASSUME_CALL"), Path.pushAndRefine(c);
2 },
), createOperationDescriptor("ASSUME_CALL"),
writable: true, 2
enumerable: false, ),
configurable: true, 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, // 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. // 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 // 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. // 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). // The function must not have side effects, and it must not access any state (besides the supplied arguments).
global.$DefineOwnProperty("__residual", { global.$DefineOwnProperty(
value: createNativeFunctionForResidualCall(false), "__residual",
writable: true, new PropertyDescriptor({
enumerable: false, value: createNativeFunctionForResidualCall(false),
configurable: true, 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 // 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. // and rewrite the function body to do the same thing as the original residual function.
global.$DefineOwnProperty("__residual_unsafe", { global.$DefineOwnProperty(
value: createNativeFunctionForResidualCall(true), "__residual_unsafe",
writable: true, new PropertyDescriptor({
enumerable: false, value: createNativeFunctionForResidualCall(true),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// Internal helper function for tests. // Internal helper function for tests.
// __isAbstract(value) checks if a given value is abstract. // __isAbstract(value) checks if a given value is abstract.
global.$DefineOwnProperty("__isAbstract", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => { "__isAbstract",
return new BooleanValue(realm, value instanceof AbstractValue); new PropertyDescriptor({
}), value: new NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => {
writable: true, return new BooleanValue(realm, value instanceof AbstractValue);
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// __makePartial(object) marks an (abstract) object as partial. // __makePartial(object) marks an (abstract) object as partial.
global.$DefineOwnProperty("__makePartial", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => { "__makePartial",
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) { new PropertyDescriptor({
object.makePartial(); value: new NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => {
return object; if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
} object.makePartial();
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object"); return object;
}), }
writable: true, throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__makeFinal", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__makeFinal", "__makeFinal", 1, (context, [object]) => { "__makeFinal",
if (object instanceof ObjectValue || (object instanceof AbstractObjectValue && !object.values.isTop())) { new PropertyDescriptor({
object.makeFinal(); value: new NativeFunctionValue(realm, "global.__makeFinal", "__makeFinal", 1, (context, [object]) => {
return object; if (object instanceof ObjectValue || (object instanceof AbstractObjectValue && !object.values.isTop())) {
} object.makeFinal();
throw realm.createErrorThrowCompletion( return object;
realm.intrinsics.TypeError, }
"not an object or abstract object value (non-top)" throw realm.createErrorThrowCompletion(
); realm.intrinsics.TypeError,
}), "not an object or abstract object value (non-top)"
writable: true, );
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// __makeSimple(object) marks an (abstract) object as one that has no getters or setters. // __makeSimple(object) marks an (abstract) object as one that has no getters or setters.
global.$DefineOwnProperty("__makeSimple", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => { "__makeSimple",
if (object instanceof AbstractObjectValue || object instanceof ObjectValue) { new PropertyDescriptor({
object.makeSimple(option); value: new NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => {
return object; if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
} object.makeSimple(option);
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object"); return object;
}), }
writable: true, throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
// Helper function that emits a check whether a given object property has a particular value. // Helper function that emits a check whether a given object property has a particular value.
global.$DefineOwnProperty("__assumeDataProperty", { global.$DefineOwnProperty(
value: new NativeFunctionValue( "__assumeDataProperty",
realm, new PropertyDescriptor({
"global.__assumeDataProperty", value: new NativeFunctionValue(
"__assumeDataProperty", realm,
3, "global.__assumeDataProperty",
(context, [object, propertyName, value, invariantOptions]) => { "__assumeDataProperty",
if (!realm.useAbstractInterpretation) { 3,
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial"); (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);
} }
realm.generator = undefined; // don't emit code during the following $Set call
// casting to due to Flow workaround above if (object instanceof AbstractObjectValue || object instanceof ObjectValue) {
(object: any).$Set(key, value, object); let generator = realm.generator;
realm.generator = generator; invariant(generator);
if (object.intrinsicName) realm.rebuildObjectProperty(object, key, value, object.intrinsicName);
return context.$Realm.intrinsics.undefined; 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"); global.$DefineOwnProperty(
} "__IntrospectionError",
), new PropertyDescriptor({
writable: true, value: realm.intrinsics.__IntrospectionError,
enumerable: false, writable: true,
configurable: true, enumerable: false,
}); configurable: true,
})
);
global.$DefineOwnProperty("__IntrospectionError", { global.$DefineOwnProperty(
value: realm.intrinsics.__IntrospectionError, "__isIntegral",
writable: true, new PropertyDescriptor({
enumerable: false, value: new NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => {
configurable: true, return new BooleanValue(realm, value instanceof IntegralValue);
}); }),
writable: true,
enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__isIntegral", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => { "__describe",
return new BooleanValue(realm, value instanceof IntegralValue); new PropertyDescriptor({
}), value: new NativeFunctionValue(realm, "global.__describe", "__describe", 1, (context, [value]) => {
writable: true, return new StringValue(realm, describeValue(value));
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__describe", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__describe", "__describe", 1, (context, [value]) => { "__fatal",
return new StringValue(realm, describeValue(value)); new PropertyDescriptor({
}), value: new NativeFunctionValue(realm, "global.__fatal", "__fatal", 0, (context, []) => {
writable: true, throw new FatalError();
enumerable: false, }),
configurable: true, writable: true,
}); enumerable: false,
configurable: true,
})
);
global.$DefineOwnProperty("__fatal", { global.$DefineOwnProperty(
value: new NativeFunctionValue(realm, "global.__fatal", "__fatal", 0, (context, []) => { "__eagerlyRequireModuleDependencies",
throw new FatalError(); new PropertyDescriptor({
}), value: new NativeFunctionValue(
writable: true, realm,
enumerable: false, "global.__eagerlyRequireModuleDependencies",
configurable: true, "__eagerlyRequireModuleDependencies",
}); 1,
(context, [functionValue]) => {
global.$DefineOwnProperty("__eagerlyRequireModuleDependencies", { if (!IsCallable(realm, functionValue) || !(functionValue instanceof FunctionValue))
value: new NativeFunctionValue( throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be callable function");
realm, let functionCall: void | ((thisArgument: Value, argumentsList: Array<Value>) => Value) = functionValue.$Call;
"global.__eagerlyRequireModuleDependencies", if (typeof functionCall !== "function") {
"__eagerlyRequireModuleDependencies", throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be directly callable");
1, }
(context, [functionValue]) => { let old = realm.eagerlyRequireModuleDependencies;
if (!IsCallable(realm, functionValue) || !(functionValue instanceof FunctionValue)) realm.eagerlyRequireModuleDependencies = true;
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be callable function"); try {
let functionCall: void | ((thisArgument: Value, argumentsList: Array<Value>) => Value) = functionValue.$Call; return functionCall(realm.intrinsics.undefined, []);
if (typeof functionCall !== "function") { } finally {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be directly callable"); realm.eagerlyRequireModuleDependencies = old;
}
} }
let old = realm.eagerlyRequireModuleDependencies; ),
realm.eagerlyRequireModuleDependencies = true; writable: true,
try { enumerable: false,
return functionCall(realm.intrinsics.undefined, []); configurable: true,
} finally { })
realm.eagerlyRequireModuleDependencies = old; );
}
}
),
writable: true,
enumerable: false,
configurable: true,
});
} }

View File

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

View File

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

View File

@ -38,6 +38,7 @@ import invariant from "../invariant.js";
import parse from "../utils/parse.js"; import parse from "../utils/parse.js";
import traverseFast from "../utils/traverse-fast.js"; import traverseFast from "../utils/traverse-fast.js";
import type { BabelNodeIdentifier, BabelNodeLVal, BabelNodeFunctionDeclaration } from "@babel/types"; import type { BabelNodeIdentifier, BabelNodeLVal, BabelNodeFunctionDeclaration } from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
const allElementTypes = ["Undefined", "Null", "Boolean", "String", "Symbol", "Number", "Object"]; const allElementTypes = ["Undefined", "Null", "Boolean", "String", "Symbol", "Number", "Object"];
@ -69,12 +70,17 @@ export class CreateImplementation {
let length = value.value.length; let length = value.value.length;
// 10. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). // 10. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).
Properties.DefinePropertyOrThrow(realm, S, "length", { Properties.DefinePropertyOrThrow(
value: new NumberValue(realm, length), realm,
writable: false, S,
enumerable: false, "length",
configurable: false, new PropertyDescriptor({
}); value: new NumberValue(realm, length),
writable: false,
enumerable: false,
configurable: false,
})
);
// 11. Return S. // 11. Return S.
return S; return S;
@ -307,12 +313,17 @@ export class CreateImplementation {
A.setExtensible(true); A.setExtensible(true);
// 10. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}). // 10. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor{[[Value]]: length, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.OrdinaryDefineOwnProperty(realm, A, "length", { Properties.OrdinaryDefineOwnProperty(
value: new NumberValue(realm, length), realm,
writable: true, A,
enumerable: false, "length",
configurable: false, new PropertyDescriptor({
}); value: new NumberValue(realm, length),
writable: true,
enumerable: false,
configurable: false,
})
);
// 11. Return A. // 11. Return A.
return A; return A;
@ -358,12 +369,17 @@ export class CreateImplementation {
// 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len,
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "length", { Properties.DefinePropertyOrThrow(
value: new NumberValue(realm, len), realm,
writable: true, obj,
enumerable: false, "length",
configurable: true, new PropertyDescriptor({
}); value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
})
);
// 5. Let index be 0. // 5. Let index be 0.
let index = 0; let index = 0;
@ -382,21 +398,31 @@ export class CreateImplementation {
// 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]: // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:
// %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). // %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, realm.intrinsics.SymbolIterator, { Properties.DefinePropertyOrThrow(
value: realm.intrinsics.ArrayProto_values, realm,
writable: true, obj,
enumerable: false, realm.intrinsics.SymbolIterator,
configurable: true, new PropertyDescriptor({
}); value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
})
);
// 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: // 8. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]:
// %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}). // %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, obj, "callee", { Properties.DefinePropertyOrThrow(
get: realm.intrinsics.ThrowTypeError, realm,
set: realm.intrinsics.ThrowTypeError, obj,
enumerable: false, "callee",
configurable: false, new PropertyDescriptor({
}); get: realm.intrinsics.ThrowTypeError,
set: realm.intrinsics.ThrowTypeError,
enumerable: false,
configurable: false,
})
);
// 10. Return obj. // 10. Return obj.
return obj; return obj;
@ -442,7 +468,7 @@ export class CreateImplementation {
obj.setExtensible(true); obj.setExtensible(true);
// 12. Let map be ObjectCreate(null). // 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. // 13. Set the [[ParameterMap]] internal slot of obj to map.
obj.$ParameterMap = map; obj.$ParameterMap = map;
@ -473,12 +499,17 @@ export class CreateImplementation {
// 18. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, // 18. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len,
// [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "length", { Properties.DefinePropertyOrThrow(
value: new NumberValue(realm, len), realm,
writable: true, obj,
enumerable: false, "length",
configurable: true, new PropertyDescriptor({
}); value: new NumberValue(realm, len),
writable: true,
enumerable: false,
configurable: true,
})
);
// 19. Let mappedNames be an empty List. // 19. Let mappedNames be an empty List.
let mappedNames = []; let mappedNames = [];
@ -506,12 +537,15 @@ export class CreateImplementation {
// 3. Perform map.[[DefineOwnProperty]](! ToString(index), PropertyDescriptor{[[Set]]: p, [[Get]]: g, // 3. Perform map.[[DefineOwnProperty]](! ToString(index), PropertyDescriptor{[[Set]]: p, [[Get]]: g,
// [[Enumerable]]: false, [[Configurable]]: true}). // [[Enumerable]]: false, [[Configurable]]: true}).
map.$DefineOwnProperty(new StringValue(realm, index + ""), { map.$DefineOwnProperty(
set: p, new StringValue(realm, index + ""),
get: g, new PropertyDescriptor({
enumerable: false, set: p,
configurable: true, get: g,
}); enumerable: false,
configurable: true,
})
);
} }
} }
@ -521,21 +555,31 @@ export class CreateImplementation {
// 22. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]: // 22. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {[[Value]]:
// %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). // %ArrayProto_values%, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, realm.intrinsics.SymbolIterator, { Properties.DefinePropertyOrThrow(
value: realm.intrinsics.ArrayProto_values, realm,
writable: true, obj,
enumerable: false, realm.intrinsics.SymbolIterator,
configurable: true, new PropertyDescriptor({
}); value: realm.intrinsics.ArrayProto_values,
writable: true,
enumerable: false,
configurable: true,
})
);
// 23. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Value]]: // 23. Perform ! DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Value]]:
// func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}). // func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, obj, "callee", { Properties.DefinePropertyOrThrow(
value: func, realm,
writable: true, obj,
enumerable: false, "callee",
configurable: true, new PropertyDescriptor({
}); value: func,
writable: true,
enumerable: false,
configurable: true,
})
);
// 24. Return obj. // 24. Return obj.
return obj; return obj;
@ -549,12 +593,12 @@ export class CreateImplementation {
invariant(IsPropertyKey(realm, P), "Not a property key"); invariant(IsPropertyKey(realm, P), "Not a property key");
// 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}. // 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}.
let newDesc = { let newDesc = new PropertyDescriptor({
value: V, value: V,
writable: true, writable: true,
enumerable: true, enumerable: true,
configurable: true, configurable: true,
}; });
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc). // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
return O.$DefineOwnProperty(P, newDesc); return O.$DefineOwnProperty(P, newDesc);
@ -601,7 +645,7 @@ export class CreateImplementation {
let desc = from.$GetOwnProperty(nextKey); let desc = from.$GetOwnProperty(nextKey);
// If desc is not undefined and desc.[[Enumerable]] is true, then // 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 be ? Get(from, nextKey).
let propValue = Get(realm, from, nextKey); let propValue = Get(realm, from, nextKey);
// Perform ! CreateDataProperty(target, nextKey, propValue). // Perform ! CreateDataProperty(target, nextKey, propValue).
@ -624,12 +668,12 @@ export class CreateImplementation {
invariant(IsPropertyKey(realm, P), "Not a property key"); invariant(IsPropertyKey(realm, P), "Not a property key");
// 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}. // 3. Let newDesc be the PropertyDescriptor{[[Value]]: V, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}.
let newDesc = { let newDesc = new PropertyDescriptor({
value: V, value: V,
writable: true, writable: true,
enumerable: false, enumerable: false,
configurable: true, configurable: true,
}; });
// 4. Return ? O.[[DefineOwnProperty]](P, newDesc). // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
return O.$DefineOwnProperty(P, newDesc); return O.$DefineOwnProperty(P, newDesc);
@ -903,12 +947,17 @@ export class CreateImplementation {
prototype.originalConstructor = F; prototype.originalConstructor = F;
// b. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}). // b. Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, F, "prototype", { Properties.DefinePropertyOrThrow(
value: prototype, realm,
writable: true, F,
enumerable: false, "prototype",
configurable: false, new PropertyDescriptor({
}); value: prototype,
writable: true,
enumerable: false,
configurable: false,
})
);
} else { } else {
// 28. Else, perform MakeConstructor(F). // 28. Else, perform MakeConstructor(F).
MakeConstructor(realm, 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, BabelNodeWithStatement,
} from "@babel/types"; } from "@babel/types";
import * as t from "@babel/types"; import * as t from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
function InternalCall( function InternalCall(
realm: Realm, realm: Realm,
@ -756,12 +757,17 @@ export class FunctionImplementation {
} }
// 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}). // 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
return Properties.DefinePropertyOrThrow(realm, F, "name", { return Properties.DefinePropertyOrThrow(
value: name, realm,
enumerable: false, F,
writable: false, "name",
configurable: true, new PropertyDescriptor({
}); value: name,
enumerable: false,
writable: false,
configurable: true,
})
);
} }
// ECMA262 9.2.3 // 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}). // 3. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
Properties.DefinePropertyOrThrow(realm, F, "length", { Properties.DefinePropertyOrThrow(
value: new NumberValue(realm, len), realm,
writable: false, F,
enumerable: false, "length",
configurable: true, 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. // 4. Let Strict be the value of the [[Strict]] internal slot of F.
let Strict = F.$Strict; let Strict = F.$Strict;
if (!Strict) { if (!Strict) {
Properties.DefinePropertyOrThrow(realm, F, "caller", { Properties.DefinePropertyOrThrow(
value: new UndefinedValue(realm), realm,
writable: true, F,
enumerable: false, "caller",
configurable: true, 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. // 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; let thrower = realm.intrinsics.ThrowTypeError;
invariant(thrower); invariant(thrower);
let desc = { let desc = new PropertyDescriptor({
get: thrower, get: thrower,
set: thrower, set: thrower,
enumerable: false, enumerable: false,
configurable: true, configurable: true,
}; });
// 3. Perform ! DefinePropertyOrThrow(F, "caller", 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); Properties.DefinePropertyOrThrow(realm, F, "caller", desc);
// 4. Return ! DefinePropertyOrThrow(F, "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: true}). // 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. // 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" // See 16.2 "Forbidden Extensions"
if (!Strict && kind === "normal") { if (!Strict && kind === "normal") {
Properties.DefinePropertyOrThrow(realm, F, "arguments", { Properties.DefinePropertyOrThrow(
value: realm.intrinsics.undefined, realm,
enumerable: false, F,
writable: true, "arguments",
configurable: true, new PropertyDescriptor({
}); value: realm.intrinsics.undefined,
enumerable: false,
writable: true,
configurable: true,
})
);
} }
// 5. Return FunctionInitialize(F, kind, ParameterList, Body, Scope). // 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 invariant from "../invariant.js";
import type { BabelNodeTemplateLiteral } from "@babel/types"; import type { BabelNodeTemplateLiteral } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js"; import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
// ECMA262 7.3.22 // ECMA262 7.3.22
export function GetFunctionRealm(realm: Realm, obj: ObjectValue): Realm { export function GetFunctionRealm(realm: Realm, obj: ObjectValue): Realm {
@ -97,7 +98,10 @@ export function OrdinaryGet(
let prop = O.unknownProperty; let prop = O.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined && O.$GetOwnProperty(P) === undefined) { if (prop !== undefined && prop.descriptor !== undefined && O.$GetOwnProperty(P) === undefined) {
let desc = prop.descriptor; 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; let val = desc.value;
invariant(val instanceof AbstractValue); invariant(val instanceof AbstractValue);
let propValue; let propValue;
@ -129,7 +133,7 @@ export function OrdinaryGet(
// 2. Let desc be ? O.[[GetOwnProperty]](P). // 2. Let desc be ? O.[[GetOwnProperty]](P).
let desc = 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 // joined descriptors need special treatment
let joinCondition = desc.joinCondition; let joinCondition = desc.joinCondition;
@ -422,6 +426,10 @@ export function OrdinaryGetPartial(
if (prop !== undefined) { if (prop !== undefined) {
let desc = prop.descriptor; let desc = prop.descriptor;
if (desc !== undefined) { if (desc !== undefined) {
invariant(
desc instanceof PropertyDescriptor,
"unknown properties are only created with Set and have equal descriptors"
);
let val = desc.value; let val = desc.value;
invariant(val instanceof AbstractValue); invariant(val instanceof AbstractValue);
if (val.kind === "widened numeric property") { if (val.kind === "widened numeric property") {
@ -437,6 +445,7 @@ export function OrdinaryGetPartial(
for (let [key, propertyBinding] of O.properties) { for (let [key, propertyBinding] of O.properties) {
let desc = propertyBinding.descriptor; let desc = propertyBinding.descriptor;
if (desc === undefined) continue; // deleted 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 invariant(desc.value !== undefined); // otherwise this is not simple
let val = desc.value; let val = desc.value;
invariant(val instanceof Value); invariant(val instanceof Value);
@ -717,23 +726,29 @@ export function GetTemplateObject(realm: Realm, templateLiteral: BabelNodeTempla
let cookedValue = new StringValue(realm, cookedStrings[index]); let cookedValue = new StringValue(realm, cookedStrings[index]);
// c. Call template.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: cookedValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}). // c. Call template.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: cookedValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
template.$DefineOwnProperty(prop, { template.$DefineOwnProperty(
value: cookedValue, prop,
writable: false, new PropertyDescriptor({
enumerable: true, value: cookedValue,
configurable: false, writable: false,
}); enumerable: true,
configurable: false,
})
);
// d. Let rawValue be the String value rawStrings[index]. // d. Let rawValue be the String value rawStrings[index].
let rawValue = new StringValue(realm, rawStrings[index]); let rawValue = new StringValue(realm, rawStrings[index]);
// e. Call rawObj.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: rawValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}). // e. Call rawObj.[[DefineOwnProperty]](prop, PropertyDescriptor{[[Value]]: rawValue, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false}).
rawObj.$DefineOwnProperty(prop, { rawObj.$DefineOwnProperty(
value: rawValue, prop,
writable: false, new PropertyDescriptor({
enumerable: true, value: rawValue,
configurable: false, writable: false,
}); enumerable: true,
configurable: false,
})
);
// f. Let index be index+1. // f. Let index be index+1.
index = index + 1; index = index + 1;
@ -743,12 +758,15 @@ export function GetTemplateObject(realm: Realm, templateLiteral: BabelNodeTempla
SetIntegrityLevel(realm, rawObj, "frozen"); SetIntegrityLevel(realm, rawObj, "frozen");
// 12. Call template.[[DefineOwnProperty]]("raw", PropertyDescriptor{[[Value]]: rawObj, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}). // 12. Call template.[[DefineOwnProperty]]("raw", PropertyDescriptor{[[Value]]: rawObj, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false}).
template.$DefineOwnProperty("raw", { template.$DefineOwnProperty(
value: rawObj, "raw",
writable: false, new PropertyDescriptor({
enumerable: false, value: rawObj,
configurable: false, writable: false,
}); enumerable: false,
configurable: false,
})
);
// 13. Perform SetIntegrityLevel(template, "frozen"). // 13. Perform SetIntegrityLevel(template, "frozen").
SetIntegrityLevel(realm, template, "frozen"); SetIntegrityLevel(realm, template, "frozen");
@ -781,7 +799,7 @@ export function GetFromArrayWithWidenedNumericProperty(
if (prototypeBinding !== undefined) { if (prototypeBinding !== undefined) {
let descriptor = prototypeBinding.descriptor; let descriptor = prototypeBinding.descriptor;
// ensure we are accessing a built-in native function // 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; return descriptor.value;
} }
} }

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import { IsExtensible, IsDataDescriptor, IsAccessorDescriptor } from "./index.js
import { Properties } from "../singletons.js"; import { Properties } from "../singletons.js";
import { FatalError } from "../errors.js"; import { FatalError } from "../errors.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
type IntegrityLevels = "sealed" | "frozen"; 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, // a. Repeat for each element k of keys,
for (let k of keys) { for (let k of keys) {
// i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor{[[Configurable]]: false}). // i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor{[[Configurable]]: false}).
Properties.DefinePropertyOrThrow(realm, O, k, { Properties.DefinePropertyOrThrow(
configurable: false, realm,
}); O,
k,
new PropertyDescriptor({
configurable: false,
})
);
} }
} else if (level === "frozen") { } else if (level === "frozen") {
// 7. Else level is "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 // ii. If currentDesc is not undefined, then
if (currentDesc) { if (currentDesc) {
Properties.ThrowIfMightHaveBeenDeleted(currentDesc.value); Properties.ThrowIfMightHaveBeenDeleted(currentDesc);
let desc; let desc;
// 1. If IsAccessorDescriptor(currentDesc) is true, then // 1. If IsAccessorDescriptor(currentDesc) is true, then
if (IsAccessorDescriptor(realm, currentDesc)) { if (IsAccessorDescriptor(realm, currentDesc)) {
// a. Let desc be the PropertyDescriptor{[[Configurable]]: false}. // a. Let desc be the PropertyDescriptor{[[Configurable]]: false}.
desc = { configurable: false }; desc = new PropertyDescriptor({ configurable: false });
} else { } else {
// 2. Else, // 2. Else,
// b. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }. // 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). // 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 // b. If currentDesc is not undefined, then
if (currentDesc) { if (currentDesc) {
Properties.ThrowIfMightHaveBeenDeleted(currentDesc.value); Properties.ThrowIfMightHaveBeenDeleted(currentDesc);
currentDesc = currentDesc.throwIfNotConcrete(realm);
// i. If currentDesc.[[Configurable]] is true, return false. // i. If currentDesc.[[Configurable]] is true, return false.
if (currentDesc.configurable === 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 invariant from "../invariant.js";
import { HasName, HasCompatibleType } from "./has.js"; import { HasName, HasCompatibleType } from "./has.js";
import type { BabelNodeExpression, BabelNodeCallExpression, BabelNodeLVal, BabelNodeClassMethod } from "@babel/types"; import type { BabelNodeExpression, BabelNodeCallExpression, BabelNodeLVal, BabelNodeClassMethod } from "@babel/types";
import { PropertyDescriptor } from "../descriptors.js";
// ECMA262 22.1.3.1.1 // ECMA262 22.1.3.1.1
export function IsConcatSpreadable(realm: Realm, _O: Value): boolean { 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 // 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. // 1. If Desc is undefined, return false.
if (!Desc) return false; if (!Desc) return false;
@ -63,29 +64,47 @@ export function IsGenericDescriptor(realm: Realm, Desc: ?Descriptor): boolean {
} }
// ECMA262 6.2.4.1 // 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. // 1. If Desc is undefined, return false.
if (!Desc) return false; if (!Desc) return false;
// 2. If both Desc.[[Get]] and Desc.[[Set]] are absent, 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. // 3. Return true.
return true; return true;
} }
// ECMA262 6.2.4.2 // 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 is undefined, return false.
if (!Desc) return false; if (!Desc) return false;
// If both Desc.[[Value]] and Desc.[[Writable]] are absent, 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.
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 // ECMA262 9.1.3.1
export function OrdinaryIsExtensible(realm: Realm, O: ObjectValue): boolean { export function OrdinaryIsExtensible(realm: Realm, O: ObjectValue): boolean {
// 1. Return the value of the [[Extensible]] internal slot of O. // 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 type { Bindings, BindingEntry, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
import { construct_empty_effects, Effects } from "../realm.js"; import { construct_empty_effects, Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js"; import type { Descriptor, PropertyBinding } from "../types.js";
import {
cloneDescriptor,
equalDescriptors,
PropertyDescriptor,
AbstractJoinedDescriptor,
InternalSlotDescriptor,
} from "../descriptors.js";
import { import {
AbruptCompletion, AbruptCompletion,
@ -26,7 +33,7 @@ import {
ReturnCompletion, ReturnCompletion,
ThrowCompletion, ThrowCompletion,
} from "../completions.js"; } 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 { Path } from "../singletons.js";
import { Generator } from "../utils/generator.js"; import { Generator } from "../utils/generator.js";
import { AbstractValue, ConcreteValue, EmptyValue, Value } from "../values/index.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 (c2.has(b.object)) return d2; // no join
if (b.descriptor !== undefined && m1.has(b)) { if (b.descriptor !== undefined && m1.has(b)) {
// property was deleted // property was deleted
d1 = cloneDescriptor(b.descriptor); d1 = cloneDescriptor(b.descriptor.throwIfNotConcrete(realm));
invariant(d1 !== undefined); invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty; d1.value = realm.intrinsics.empty;
} else { } else {
@ -378,7 +385,7 @@ export class JoinImplementation {
if (c1.has(b.object)) return d1; // no join if (c1.has(b.object)) return d1; // no join
if (b.descriptor !== undefined && m2.has(b)) { if (b.descriptor !== undefined && m2.has(b)) {
// property was deleted // property was deleted
d2 = cloneDescriptor(b.descriptor); d2 = cloneDescriptor(b.descriptor.throwIfNotConcrete(realm));
invariant(d2 !== undefined); invariant(d2 !== undefined);
d2.value = realm.intrinsics.empty; d2.value = realm.intrinsics.empty;
} else { } else {
@ -403,69 +410,79 @@ export class JoinImplementation {
let clone_with_abstract_value = (d: Descriptor) => { let clone_with_abstract_value = (d: Descriptor) => {
invariant(d === d1 || d === d2); invariant(d === d1 || d === d2);
if (!IsDataDescriptor(realm, d)) { if (!IsDataDescriptor(realm, d)) {
let d3: Descriptor = {}; return new AbstractJoinedDescriptor(joinCondition);
d3.joinCondition = joinCondition;
return d3;
} }
let dc = cloneDescriptor(d); let dc;
invariant(dc !== undefined); let dcValue;
let dcValue = dc.value; if (d instanceof InternalSlotDescriptor) {
if (Array.isArray(dcValue)) { dc = new InternalSlotDescriptor(d.value);
invariant(dcValue.length > 0); dcValue = dc.value;
let elem0 = dcValue[0]; if (Array.isArray(dcValue)) {
if (elem0 instanceof Value) { invariant(dcValue.length > 0);
dc.value = dcValue.map(e => { let elem0 = dcValue[0];
return d === d1 if (elem0 instanceof Value) {
? getAbstractValue((e: any), realm.intrinsics.empty) dc.value = dcValue.map(e => {
: getAbstractValue(realm.intrinsics.empty, (e: any)); return d === d1
}); ? getAbstractValue((e: any), realm.intrinsics.empty)
} else { : getAbstractValue(realm.intrinsics.empty, (e: any));
dc.value = dcValue.map(e => { });
let { $Key: key1, $Value: val1 } = (e: any); } else {
let key3 = dc.value = dcValue.map(e => {
d === d1 let { $Key: key1, $Value: val1 } = (e: any);
? getAbstractValue(key1, realm.intrinsics.empty) let key3 =
: getAbstractValue(realm.intrinsics.empty, key1); d === d1
let val3 = ? getAbstractValue(key1, realm.intrinsics.empty)
d === d1 : getAbstractValue(realm.intrinsics.empty, key1);
? getAbstractValue(val1, realm.intrinsics.empty) let val3 =
: getAbstractValue(realm.intrinsics.empty, val1); d === d1
return { $Key: key3, $Value: val3 }; ? getAbstractValue(val1, realm.intrinsics.empty)
}); : getAbstractValue(realm.intrinsics.empty, val1);
return { $Key: key3, $Value: val3 };
});
}
} }
} else { } else {
invariant(dcValue === undefined || dcValue instanceof Value); dc = cloneDescriptor(d.throwIfNotConcrete(realm));
dc.value = invariant(dc !== undefined);
d === d1 dcValue = dc.value;
? getAbstractValue(dcValue, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, dcValue);
} }
invariant(dcValue === undefined || dcValue instanceof Value);
dc.value =
d === d1
? getAbstractValue(dcValue, realm.intrinsics.empty)
: getAbstractValue(realm.intrinsics.empty, dcValue);
return dc; return dc;
}; };
if (d1 === undefined) { if (d1 === undefined) {
if (d2 === undefined) return undefined; if (d2 === undefined) return undefined;
// d2 is a new property created in only one branch, join with empty // d2 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d2); let d3 = clone_with_abstract_value(d2);
if (!IsDataDescriptor(realm, d2)) d3.descriptor2 = d2; if (d3 instanceof AbstractJoinedDescriptor) d3.descriptor2 = d2;
return d3; return d3;
} else if (d2 === undefined) { } else if (d2 === undefined) {
invariant(d1 !== undefined); invariant(d1 !== undefined);
// d1 is a new property created in only one branch, join with empty // d1 is a new property created in only one branch, join with empty
let d3 = clone_with_abstract_value(d1); let d3 = clone_with_abstract_value(d1);
if (!IsDataDescriptor(realm, d1)) d3.descriptor1 = d1; if (d3 instanceof AbstractJoinedDescriptor) d3.descriptor1 = d1;
return d3; return d3;
} else { } 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); let dc = cloneDescriptor(d1);
invariant(dc !== undefined); 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; return dc;
} }
let d3: Descriptor = {}; if (d1 instanceof InternalSlotDescriptor && d2 instanceof InternalSlotDescriptor) {
d3.joinCondition = joinCondition; return new InternalSlotDescriptor(this.joinValues(realm, d1.value, d2.value, getAbstractValue));
d3.descriptor1 = d1; }
d3.descriptor2 = d2; return new AbstractJoinedDescriptor(joinCondition, d1, d2);
return d3;
} }
} }

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ import {
} from "../values/index.js"; } from "../values/index.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { createOperationDescriptor } from "../utils/generator.js"; import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
type ElementConvType = { type ElementConvType = {
Int8: (Realm, numberOrValue) => number, Int8: (Realm, numberOrValue) => number,
@ -278,7 +279,7 @@ export class ToImplementation {
} }
// 2. Let desc be a new Property Descriptor that initially has no fields. // 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"). // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
let hasEnumerable = HasProperty(realm, 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 // 15. If either desc.[[Get]] or desc.[[Set]] is present, then
if (desc.get || desc.set) { if (desc.get || desc.set) {
// a. If either desc.[[Value]] or desc.[[Writable]] is present, throw a TypeError exception. // 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); 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 type { Bindings, BindingEntry, EvaluationResult, PropertyBindings, CreatedObjects, Realm } from "../realm.js";
import { Effects } from "../realm.js"; import { Effects } from "../realm.js";
import type { Descriptor, PropertyBinding } from "../types.js"; import type { Descriptor, PropertyBinding } from "../types.js";
import { cloneDescriptor, equalDescriptors, PropertyDescriptor } from "../descriptors.js";
import { AbruptCompletion, JoinedNormalAndAbruptCompletions, SimpleNormalCompletion } from "../completions.js"; import { AbruptCompletion, JoinedNormalAndAbruptCompletions, SimpleNormalCompletion } from "../completions.js";
import { Reference } from "../environment.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 { Generator, createOperationDescriptor } from "../utils/generator.js";
import { AbstractValue, ArrayValue, EmptyValue, Value, StringValue } from "../values/index.js"; import { AbstractValue, ArrayValue, EmptyValue, Value, StringValue } from "../values/index.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { InternalSlotDescriptor } from "../descriptors.js";
export class WidenImplementation { export class WidenImplementation {
_widenArrays( _widenArrays(
@ -212,17 +214,18 @@ export class WidenImplementation {
if (d1 === undefined && d2 === undefined) return undefined; if (d1 === undefined && d2 === undefined) return undefined;
// If the PropertyBinding object has been freshly allocated do not widen (that happens in AbstractObjectValue) // If the PropertyBinding object has been freshly allocated do not widen (that happens in AbstractObjectValue)
if (d1 === undefined) { if (d1 === undefined) {
invariant(d2 !== undefined);
if (c2.has(b.object)) return d2; // no widen if (c2.has(b.object)) return d2; // no widen
if (b.descriptor !== undefined && m1.has(b)) { if (b.descriptor !== undefined && m1.has(b)) {
// property was present in (n-1)th iteration and deleted in nth iteration // 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); invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty; d1.value = realm.intrinsics.empty;
} else { } else {
// no write to property in nth iteration, use the value from the (n-1)th iteration // no write to property in nth iteration, use the value from the (n-1)th iteration
d1 = b.descriptor; d1 = b.descriptor;
if (d1 === undefined) { if (d1 === undefined) {
d1 = cloneDescriptor(d2); d1 = cloneDescriptor(d2.throwIfNotConcrete(realm));
invariant(d1 !== undefined); invariant(d1 !== undefined);
d1.value = realm.intrinsics.empty; d1.value = realm.intrinsics.empty;
} }
@ -232,7 +235,7 @@ export class WidenImplementation {
if (c1.has(b.object)) return d1; // no widen if (c1.has(b.object)) return d1; // no widen
if (m2.has(b)) { if (m2.has(b)) {
// property was present in nth iteration and deleted in (n+1)th iteration // property was present in nth iteration and deleted in (n+1)th iteration
d2 = cloneDescriptor(d1); d2 = cloneDescriptor(d1.throwIfNotConcrete(realm));
invariant(d2 !== undefined); invariant(d2 !== undefined);
d2.value = realm.intrinsics.empty; d2.value = realm.intrinsics.empty;
} else { } else {
@ -277,8 +280,7 @@ export class WidenImplementation {
// before the loop commences, otherwise the memberExpression will result in an undefined value. // before the loop commences, otherwise the memberExpression will result in an undefined value.
let generator = realm.generator; let generator = realm.generator;
invariant(generator !== undefined); invariant(generator !== undefined);
let initVal = (b.descriptor && b.descriptor.value) || realm.intrinsics.empty; let initVal = (b.descriptor && b.descriptor.throwIfNotConcrete(realm).value) || realm.intrinsics.empty;
if (!(initVal instanceof Value)) throw new FatalError("todo: handle internal properties");
if (!(initVal instanceof EmptyValue)) { if (!(initVal instanceof EmptyValue)) {
if (key === "length" && b.object instanceof ArrayValue) { if (key === "length" && b.object instanceof ArrayValue) {
// do nothing, the array length will already be initialized // do nothing, the array length will already be initialized
@ -311,7 +313,8 @@ export class WidenImplementation {
return this.widenMaps(m1, m2, widen); 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) { if (d1 === undefined) {
// d2 is a property written to only in the (n+1)th iteration // 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. if (!IsDataDescriptor(realm, d2)) return d2; // accessor properties need not be widened.
@ -319,9 +322,12 @@ export class WidenImplementation {
invariant(dc !== undefined); invariant(dc !== undefined);
let d2value = dc.value; let d2value = dc.value;
invariant(d2value !== undefined); // because IsDataDescriptor is true for d2/dc 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; return dc;
} else { } else {
d1 = d1.throwIfNotConcrete(realm);
if (equalDescriptors(d1, d2)) { if (equalDescriptors(d1, d2)) {
if (!IsDataDescriptor(realm, d1)) return d1; // identical accessor properties need not be widened. 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. // 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); invariant(d1value !== undefined);
let d2value = d2.value; let d2value = d2.value;
invariant(d2value !== undefined); 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; return dc;
} }
//todo: #1174 if we get here, the loop body contains a call to create a property and different iterations //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 c2: CreatedObjects
): boolean { ): boolean {
let containsPropertyBinding = (d1: void | Descriptor, d2: void | Descriptor) => { 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) { if (v1 === undefined) {
return v2 === undefined; return v2 === undefined;
} }

View File

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

View File

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

View File

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

View File

@ -62,7 +62,7 @@ export function prepackSources(
return { code: "", map: undefined }; return { code: "", map: undefined };
} else { } else {
let serializer = new Serializer(realm, getSerializerOptions(options)); 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 //Turn off the debugger if there is one
if (realm.debuggerInstance) { if (realm.debuggerInstance) {

View File

@ -38,6 +38,7 @@ import type { ClassComponentMetadata, ReactComponentTreeConfig } from "../types.
import type { ReactEvaluatedNode } from "../serializer/types.js"; import type { ReactEvaluatedNode } from "../serializer/types.js";
import { FatalError } from "../errors.js"; import { FatalError } from "../errors.js";
import { type ComponentModel, ShapeInformation } from "../utils/ShapeInformation.js"; import { type ComponentModel, ShapeInformation } from "../utils/ShapeInformation.js";
import { PropertyDescriptor } from "../descriptors.js";
const lifecycleMethods = new Set([ const lifecycleMethods = new Set([
"componentWillUnmount", "componentWillUnmount",
@ -231,9 +232,12 @@ export function createClassInstanceForFirstRenderOnly(
newState.makeFinal(); newState.makeFinal();
for (let [key, binding] of stateToUpdate.properties) { for (let [key, binding] of stateToUpdate.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) { if (binding && binding.descriptor) {
let value = getProperty(realm, stateToUpdate, key); invariant(binding.descriptor instanceof PropertyDescriptor);
hardModifyReactObjectPropertyBinding(realm, newState, key, value); 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 { computeBinary } from "../evaluators/BinaryExpression.js";
import { CompilerDiagnostic, FatalError } from "../errors.js"; import { CompilerDiagnostic, FatalError } from "../errors.js";
import { createOperationDescriptor } from "../utils/generator.js"; import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
function createPropsObject( function createPropsObject(
realm: Realm, realm: Realm,
@ -110,8 +111,11 @@ function createPropsObject(
const applyProperties = () => { const applyProperties = () => {
if (config instanceof ObjectValue) { if (config instanceof ObjectValue) {
for (let [propKey, binding] of config.properties) { for (let [propKey, binding] of config.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) { if (binding && binding.descriptor) {
setProp(propKey, Get(realm, config, propKey)); 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 // first see if we can apply all the defaultProps without needing the helper
if (defaultProps instanceof ObjectValue && !defaultProps.isPartialObject()) { if (defaultProps instanceof ObjectValue && !defaultProps.isPartialObject()) {
for (let [propName, binding] of defaultProps.properties) { for (let [propName, binding] of defaultProps.properties) {
if (binding.descriptor !== undefined && binding.descriptor.value !== realm.intrinsics.undefined) { if (binding.descriptor !== undefined) {
// see if we have this on our props object invariant(binding.descriptor instanceof PropertyDescriptor);
let propBinding = props.properties.get(propName); if (binding.descriptor.value !== realm.intrinsics.undefined) {
// if the binding exists and value is abstract, it might be undefined // see if we have this on our props object
// so in that case we need the helper, otherwise we can continue let propBinding = props.properties.get(propName);
if ( // if the binding exists and value is abstract, it might be undefined
propBinding !== undefined && // so in that case we need the helper, otherwise we can continue
!(propBinding.descriptor && propBinding.descriptor.value instanceof AbstractValue) if (
) { propBinding !== undefined &&
defaultPropsEvaluated++; !(
// if the value we have is undefined, we can apply the defaultProp propBinding.descriptor instanceof PropertyDescriptor &&
if (propBinding.descriptor && propBinding.descriptor.value === realm.intrinsics.undefined) { propBinding.descriptor.value instanceof AbstractValue
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName)); )
) {
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 // as the helper function applies defaultProps on values that are undefined or do not
// exist // exist
for (let [propName, binding] of props.properties) { for (let [propName, binding] of props.properties) {
if (binding.descriptor !== undefined && binding.descriptor.value === realm.intrinsics.undefined) { if (binding.descriptor !== undefined) {
invariant(defaultProps instanceof AbstractObjectValue || defaultProps instanceof ObjectValue); invariant(binding.descriptor instanceof PropertyDescriptor);
hardModifyReactObjectPropertyBinding(realm, props, propName, Get(realm, defaultProps, propName)); 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 // 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"); invariant(false, "TODO: we need to eventually support this");
} else if (defaultProps instanceof ObjectValue) { } else if (defaultProps instanceof ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) { for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) { if (binding && binding.descriptor) {
if (Get(realm, props, propKey) === realm.intrinsics.undefined) { invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable && Get(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, Get(realm, defaultProps, propKey)); setProp(propKey, Get(realm, defaultProps, propKey));
} }
} }
@ -348,8 +364,9 @@ export function cloneReactElement(
if (defaultProps instanceof ObjectValue) { if (defaultProps instanceof ObjectValue) {
for (let [propKey, binding] of defaultProps.properties) { for (let [propKey, binding] of defaultProps.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) { if (binding && binding.descriptor) {
if (Get(realm, props, propKey) === realm.intrinsics.undefined) { invariant(binding.descriptor instanceof PropertyDescriptor);
if (binding.descriptor.enumerable && Get(realm, props, propKey) === realm.intrinsics.undefined) {
setProp(propKey, Get(realm, defaultProps, propKey)); 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 type { ClassComponentMetadata, ReactComponentTreeConfig, ReactHint } from "../types.js";
import { handleReportedSideEffect } from "../serializer/utils.js"; import { handleReportedSideEffect } from "../serializer/utils.js";
import { createOperationDescriptor } from "../utils/generator.js"; import { createOperationDescriptor } from "../utils/generator.js";
import { PropertyDescriptor } from "../descriptors.js";
type ComponentResolutionStrategy = type ComponentResolutionStrategy =
| "NORMAL" | "NORMAL"
@ -127,6 +128,7 @@ function setContextCurrentValue(contextObject: ObjectValue | AbstractObjectValue
let binding = contextObject.properties.get("currentValue"); let binding = contextObject.properties.get("currentValue");
if (binding && binding.descriptor) { if (binding && binding.descriptor) {
invariant(binding.descriptor instanceof PropertyDescriptor);
binding.descriptor.value = value; binding.descriptor.value = value;
} else { } else {
invariant(false, "setContextCurrentValue failed to set the currentValue"); invariant(false, "setContextCurrentValue failed to set the currentValue");
@ -1477,15 +1479,18 @@ export class Reconciler {
this._findReactComponentTrees(props, evaluatedNode, treatFunctionsAs, componentType, context, branchStatus); this._findReactComponentTrees(props, evaluatedNode, treatFunctionsAs, componentType, context, branchStatus);
} else { } else {
for (let [propName, binding] of value.properties) { for (let [propName, binding] of value.properties) {
if (binding && binding.descriptor && binding.descriptor.enumerable) { if (binding && binding.descriptor) {
this._findReactComponentTrees( invariant(binding.descriptor instanceof PropertyDescriptor);
getProperty(this.realm, value, propName), if (binding.descriptor.enumerable) {
evaluatedNode, this._findReactComponentTrees(
treatFunctionsAs, getProperty(this.realm, value, propName),
componentType, evaluatedNode,
context, treatFunctionsAs,
branchStatus componentType,
); context,
branchStatus
);
}
} }
} }
} }

View File

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

View File

@ -54,7 +54,7 @@ import {
DeclarativeEnvironmentRecord, DeclarativeEnvironmentRecord,
} from "./environment.js"; } from "./environment.js";
import type { Binding } from "./environment.js"; import type { Binding } from "./environment.js";
import { cloneDescriptor, Construct } from "./methods/index.js"; import { Construct } from "./methods/index.js";
import { import {
AbruptCompletion, AbruptCompletion,
Completion, Completion,
@ -81,6 +81,12 @@ import {
Widen, Widen,
} from "./singletons.js"; } from "./singletons.js";
import type { ReactSymbolTypes } from "./react/utils.js"; import type { ReactSymbolTypes } from "./react/utils.js";
import {
cloneDescriptor,
AbstractJoinedDescriptor,
InternalSlotDescriptor,
PropertyDescriptor,
} from "./descriptors.js";
import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal } from "@babel/types"; import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal } from "@babel/types";
export type BindingEntry = { hasLeaked: boolean, value: void | Value }; export type BindingEntry = { hasLeaked: boolean, value: void | Value };
export type Bindings = Map<Binding, BindingEntry>; export type Bindings = Map<Binding, BindingEntry>;
@ -641,7 +647,7 @@ export class Realm {
invariant(globalObject instanceof ObjectValue); invariant(globalObject instanceof ObjectValue);
let binding = globalObject.properties.get("__checkedBindings"); let binding = globalObject.properties.get("__checkedBindings");
invariant(binding !== undefined); invariant(binding !== undefined);
let checkedBindingsObject = binding.descriptor && binding.descriptor.value; let checkedBindingsObject = binding.descriptor && binding.descriptor.throwIfNotConcrete(this).value;
invariant(checkedBindingsObject instanceof ObjectValue); invariant(checkedBindingsObject instanceof ObjectValue);
return checkedBindingsObject; return checkedBindingsObject;
} }
@ -662,7 +668,7 @@ export class Realm {
let id = `__propertyHasBeenChecked__${objectId}:${P}`; let id = `__propertyHasBeenChecked__${objectId}:${P}`;
let binding = this._getCheckedBindings().properties.get(id); let binding = this._getCheckedBindings().properties.get(id);
if (binding === undefined) return false; 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(); return value instanceof Value && !value.mightNotBeTrue();
} }
@ -1080,7 +1086,7 @@ export class Realm {
if (newlyCreatedObjects.has(key.object) || key.object.refuseSerialization) { if (newlyCreatedObjects.has(key.object) || key.object.refuseSerialization) {
return; return;
} }
let value = val && val.value; let value = val && val.throwIfNotConcrete(this).value;
if (value instanceof AbstractValue) { if (value instanceof AbstractValue) {
invariant(value.operationDescriptor !== undefined); invariant(value.operationDescriptor !== undefined);
let tval = gen.deriveAbstract( let tval = gen.deriveAbstract(
@ -1102,7 +1108,7 @@ export class Realm {
let path = key.pathNode; let path = key.pathNode;
let tval = tvalFor.get(key); let tval = tvalFor.get(key);
invariant(val !== undefined); invariant(val !== undefined);
let value = val.value; let value = val.throwIfNotConcrete(this).value;
invariant(value instanceof Value); invariant(value instanceof Value);
let keyKey = key.key; let keyKey = key.key;
if (typeof keyKey === "string") { if (typeof keyKey === "string") {
@ -1516,7 +1522,20 @@ export class Realm {
} }
this.callReportPropertyAccess(binding); this.callReportPropertyAccess(binding);
if (this.modifiedProperties !== undefined && !this.modifiedProperties.has(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) { for (let [key, binding] of template.properties) {
if (binding === undefined || binding.descriptor === undefined) continue; // deleted if (binding === undefined || binding.descriptor === undefined) continue; // deleted
invariant(binding.descriptor !== undefined); invariant(binding.descriptor !== undefined);
let value = binding.descriptor.value; let desc = binding.descriptor.throwIfNotConcrete(this);
Properties.ThrowIfMightHaveBeenDeleted(value); let value = desc.value;
Properties.ThrowIfMightHaveBeenDeleted(desc);
if (value === undefined) { if (value === undefined) {
AbstractValue.reportIntrospectionError(abstractValue, key); AbstractValue.reportIntrospectionError(abstractValue, key);
throw new FatalError(); 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 NumberValue || v instanceof BooleanValue) return v.value.toString();
if (v instanceof UndefinedValue) return "undefined"; if (v instanceof UndefinedValue) return "undefined";
if (v instanceof NullValue) return "null"; 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 FunctionValue) return To.ToStringPartial(realm, Get(realm, v, "name")) || "(anonymous function)";
if (v instanceof ObjectValue) return "(some object)"; if (v instanceof ObjectValue) return "(some object)";
if (v instanceof AbstractValue) return "(some abstract value)"; 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 { describeValue } from "../utils.js";
import { getAsPropertyNameExpression } from "../utils/babelhelpers.js"; import { getAsPropertyNameExpression } from "../utils/babelhelpers.js";
import { ResidualOperationSerializer } from "./ResidualOperationSerializer.js"; import { ResidualOperationSerializer } from "./ResidualOperationSerializer.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
function commentStatement(text: string) { function commentStatement(text: string) {
let s = t.emptyStatement(); let s = t.emptyStatement();
@ -317,7 +318,8 @@ export class ResidualHeapSerializer {
// TODO #2259: Make deduplication in the face of leaking work for custom accessors // TODO #2259: Make deduplication in the face of leaking work for custom accessors
let isCertainlyLeaked = !obj.mightNotBeLeakedObject(); let isCertainlyLeaked = !obj.mightNotBeLeakedObject();
let shouldDropAsAssignedProp = (descriptor: Descriptor | void) => 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 // inject properties
for (let [key, propertyBinding] of properties) { for (let [key, propertyBinding] of properties) {
@ -348,14 +350,11 @@ export class ResidualHeapSerializer {
if (obj.unknownProperty !== undefined) { if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor; let desc = obj.unknownProperty.descriptor;
if (desc !== undefined) { if (desc !== undefined) {
let val = desc.value;
invariant(val instanceof AbstractValue);
let semaphore = this._acquireOneObjectSemaphore(obj); let semaphore = this._acquireOneObjectSemaphore(obj);
this.emitter.emitNowOrAfterWaitingForDependencies( this.emitter.emitNowOrAfterWaitingForDependencies(
this._getNestedValuesFromAbstract(val, [obj]), this._getNestedValuesFromAbstractDescriptor(desc, [obj]),
() => { () => {
invariant(val instanceof AbstractValue); this._emitPropertiesWithComputedNamesDescriptor(obj, desc);
this._emitPropertiesWithComputedNames(obj, val);
if (semaphore !== undefined) semaphore.releaseOne(); if (semaphore !== undefined) semaphore.releaseOne();
}, },
this.emitter.getBody() 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> { _getNestedValuesFromAbstract(absVal: AbstractValue, values: Array<Value>): Array<Value> {
if (absVal.kind === "widened property") return values; if (absVal.kind === "widened property") return values;
if (absVal.kind === "template for prototype member expression") return values; if (absVal.kind === "template for prototype member expression") return values;
@ -477,6 +492,60 @@ export class ResidualHeapSerializer {
return values; 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 { _emitPropertiesWithComputedNames(obj: ObjectValue, absVal: AbstractValue): void {
if (absVal.kind === "widened property") return; if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return; if (absVal.kind === "template for prototype member expression") return;
@ -577,7 +646,7 @@ export class ResidualHeapSerializer {
key: string | SymbolValue | AbstractValue, key: string | SymbolValue | AbstractValue,
desc: Descriptor desc: Descriptor
): BabelNodeStatement { ): BabelNodeStatement {
if (desc.joinCondition) { if (desc instanceof AbstractJoinedDescriptor) {
let cond = this.serializeValue(desc.joinCondition); let cond = this.serializeValue(desc.joinCondition);
invariant(cond !== undefined); invariant(cond !== undefined);
let trueBody; let trueBody;
@ -603,6 +672,7 @@ export class ResidualHeapSerializer {
if (falseBody) return t.ifStatement(t.unaryExpression("!", cond), falseBody); if (falseBody) return t.ifStatement(t.unaryExpression("!", cond), falseBody);
invariant(false); invariant(false);
} }
invariant(desc instanceof PropertyDescriptor);
if (locationFunction !== undefined && this._canEmbedProperty(val, key, desc)) { if (locationFunction !== undefined && this._canEmbedProperty(val, key, desc)) {
let descValue = desc.value; let descValue = desc.value;
invariant(descValue instanceof Value); invariant(descValue instanceof Value);
@ -630,8 +700,8 @@ export class ResidualHeapSerializer {
let descriptorsKey = []; let descriptorsKey = [];
for (let boolKey of boolKeys) { for (let boolKey of boolKeys) {
if (boolKey in desc) { if ((desc: any)[boolKey] !== undefined) {
let b = desc[boolKey]; let b: boolean = (desc: any)[boolKey];
invariant(b !== undefined); invariant(b !== undefined);
descProps.push(t.objectProperty(t.identifier(boolKey), t.booleanLiteral(b))); descProps.push(t.objectProperty(t.identifier(boolKey), t.booleanLiteral(b)));
descriptorsKey.push(`${boolKey}:${b.toString()}`); descriptorsKey.push(`${boolKey}:${b.toString()}`);
@ -650,8 +720,8 @@ export class ResidualHeapSerializer {
invariant(descriptorId !== undefined); invariant(descriptorId !== undefined);
for (let descKey of valKeys) { for (let descKey of valKeys) {
if (descKey in desc) { if ((desc: any)[descKey] !== undefined) {
let descValue = desc[descKey]; let descValue: Value = (desc: any)[descKey];
invariant(descValue instanceof Value); invariant(descValue instanceof Value);
if (descValue instanceof UndefinedValue) { if (descValue instanceof UndefinedValue) {
this.serializeValue(descValue); this.serializeValue(descValue);
@ -1172,13 +1242,24 @@ export class ResidualHeapSerializer {
} }
} }
_getDescriptorValues(desc: Descriptor): Array<Value> { _getDescriptorValues(desc: void | Descriptor): Array<Value> {
if (desc.joinCondition !== undefined) return [desc.joinCondition]; if (desc === undefined) {
invariant(desc.value === undefined || desc.value instanceof Value); return [];
if (desc.value !== undefined) return [desc.value]; } else if (desc instanceof PropertyDescriptor) {
invariant(desc.get !== undefined); invariant(desc.value === undefined || desc.value instanceof Value);
invariant(desc.set !== undefined); if (desc.value !== undefined) return [desc.value];
return [desc.get, desc.set]; 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 { _deleteProperty(location: BabelNodeLVal): void {
@ -1251,6 +1332,7 @@ export class ResidualHeapSerializer {
if (propertyBinding !== undefined) { if (propertyBinding !== undefined) {
let descriptor = propertyBinding.descriptor; let descriptor = propertyBinding.descriptor;
// "descriptor === undefined" means this array item has been deleted. // "descriptor === undefined" means this array item has been deleted.
invariant(descriptor === undefined || descriptor instanceof PropertyDescriptor);
if ( if (
descriptor !== undefined && descriptor !== undefined &&
descriptor.value !== 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. // 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 { _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); 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 // TODO #2259: Make deduplication in the face of leaking work for custom accessors
let shouldDropAsAssignedProp = (descriptor: Descriptor | void) => 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) { if (val.temporalAlias !== undefined) {
return t.objectExpression(props); return t.objectExpression(props);
@ -1757,7 +1840,8 @@ export class ResidualHeapSerializer {
if (propertyBinding.pathNode !== undefined) continue; // written to inside loop if (propertyBinding.pathNode !== undefined) continue; // written to inside loop
let descriptor = propertyBinding.descriptor; 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); let serializedKey = getAsPropertyNameExpression(key);
if (this._canEmbedProperty(val, key, descriptor)) { 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 { isReactElement, isReactPropsObject, valueIsReactLibraryObject } from "../react/utils.js";
import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js"; import { ResidualReactElementVisitor } from "./ResidualReactElementVisitor.js";
import { GeneratorDAG } from "./GeneratorDAG.js"; import { GeneratorDAG } from "./GeneratorDAG.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type BindingState = {| type BindingState = {|
capturedBindings: Set<ResidualFunctionBinding>, capturedBindings: Set<ResidualFunctionBinding>,
@ -321,22 +322,17 @@ export class ResidualHeapVisitor {
if ( if (
!(obj instanceof ArrayValue) && !(obj instanceof ArrayValue) &&
!obj.mightNotBeLeakedObject() && !obj.mightNotBeLeakedObject() &&
(descriptor !== undefined && (descriptor.get === undefined && descriptor.set === undefined)) (descriptor instanceof PropertyDescriptor && (descriptor.get === undefined && descriptor.set === undefined))
) ) {
continue; continue;
}
invariant(propertyBindingValue);
this.visitObjectProperty(propertyBindingValue); this.visitObjectProperty(propertyBindingValue);
} }
// inject properties with computed names // inject properties with computed names
if (obj.unknownProperty !== undefined) { if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor; this.visitObjectPropertiesWithComputedNamesDescriptor(obj.unknownProperty.descriptor);
if (desc !== undefined) {
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
}
} }
// prototype // 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 { visitObjectPropertiesWithComputedNames(absVal: AbstractValue): void {
if (absVal.kind === "widened property") return; if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return; if (absVal.kind === "template for prototype member expression") return;
@ -407,17 +419,19 @@ export class ResidualHeapVisitor {
} }
} }
visitDescriptor(desc: Descriptor): void { visitDescriptor(desc: void | Descriptor): void {
invariant(desc.value === undefined || desc.value instanceof Value); if (desc === undefined) {
if (desc.joinCondition !== 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); desc.joinCondition = this.visitEquivalentValue(desc.joinCondition);
if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1); if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1);
if (desc.descriptor2 !== undefined) this.visitDescriptor(desc.descriptor2); 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 { visitValueArray(val: ObjectValue): void {

View File

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

View File

@ -37,6 +37,7 @@ import { handleReportedSideEffect } from "./utils.js";
import type { ArgModel } from "../types.js"; import type { ArgModel } from "../types.js";
import { optionalStringOfLocation } from "../utils/babelhelpers"; import { optionalStringOfLocation } from "../utils/babelhelpers";
import { Properties, Utils } from "../singletons.js"; import { Properties, Utils } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
type AdditionalFunctionEntry = { type AdditionalFunctionEntry = {
value: ECMAScriptSourceFunctionValue | AbstractValue, value: ECMAScriptSourceFunctionValue | AbstractValue,
@ -54,7 +55,6 @@ export class Functions {
} }
realm: Realm; realm: Realm;
// maps back from FunctionValue to the expression string
moduleTracer: ModuleTracer; moduleTracer: ModuleTracer;
writeEffects: WriteEffects; writeEffects: WriteEffects;
_noopFunction: void | ECMAScriptSourceFunctionValue; _noopFunction: void | ECMAScriptSourceFunctionValue;
@ -120,9 +120,9 @@ export class Functions {
for (let funcId of Properties.GetOwnPropertyKeysArray(realm, globalRecordedAdditionalFunctionsMap, true, false)) { for (let funcId of Properties.GetOwnPropertyKeysArray(realm, globalRecordedAdditionalFunctionsMap, true, false)) {
let property = globalRecordedAdditionalFunctionsMap.properties.get(funcId); let property = globalRecordedAdditionalFunctionsMap.properties.get(funcId);
if (property) { if (property) {
let value = property.descriptor && property.descriptor.value; invariant(property.descriptor instanceof PropertyDescriptor);
let value = property.descriptor.value;
invariant(value !== undefined); invariant(value !== undefined);
invariant(value instanceof Value);
let entry = this._optimizedFunctionEntryOfValue(value); let entry = this._optimizedFunctionEntryOfValue(value);
if (entry) recordedAdditionalFunctions.push(entry); 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) { for (let [optimizedFunctionValue, additionalEffects] of this.writeEffects) {
// CreatedObjects is all objects created by this optimized function but not // CreatedObjects is all objects created by this optimized function but not
// nested optimized functions. // nested optimized functions.
@ -383,7 +383,7 @@ export class Functions {
if (!location) return; // happens only when accessing an additional function property if (!location) return; // happens only when accessing an additional function property
if (pbs.has(pb) && !conflicts.has(location)) { if (pbs.has(pb) && !conflicts.has(location)) {
let originalLocation = 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 ? pb.descriptor.value.expressionLocation
: undefined; : undefined;
let keyString = pb.key instanceof Value ? pb.key.toDisplayString() : pb.key; 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 { ResidualHeapGraphGenerator } from "./ResidualHeapGraphGenerator";
import { Referentializer } from "./Referentializer.js"; import { Referentializer } from "./Referentializer.js";
import { Get } from "../methods/index.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 { Properties } from "../singletons.js";
import { PropertyDescriptor } from "../descriptors.js";
export class Serializer { export class Serializer {
constructor(realm: Realm, serializerOptions: SerializerOptions = {}) { constructor(realm: Realm, serializerOptions: SerializerOptions = {}) {
@ -115,7 +116,7 @@ export class Serializer {
for (let name of Properties.GetOwnPropertyKeysArray(realm, output, false, false)) { for (let name of Properties.GetOwnPropertyKeysArray(realm, output, false, false)) {
let property = output.properties.get(name); let property = output.properties.get(name);
if (!property) continue; if (!property) continue;
let value = property.descriptor && property.descriptor.value; let value = property.descriptor instanceof PropertyDescriptor && property.descriptor.value;
if (!(value instanceof Value)) continue; if (!(value instanceof Value)) continue;
generator.emitGlobalDeclaration(name, value); generator.emitGlobalDeclaration(name, value);
} }
@ -125,7 +126,8 @@ export class Serializer {
init( init(
sourceFileCollection: SourceFileCollection, sourceFileCollection: SourceFileCollection,
sourceMaps?: boolean = false, sourceMaps?: boolean = false,
onParse?: BabelNodeFile => void onParse?: BabelNodeFile => void,
onExecute?: (Realm, Map<FunctionValue, Generator>) => void
): void | SerializedResult { ): void | SerializedResult {
let realmStatistics = this.realm.statistics; let realmStatistics = this.realm.statistics;
invariant(realmStatistics instanceof SerializerStatistics, "serialization requires SerializerStatistics"); 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(() => statistics.processCollectedNestedOptimizedFunctions.measure(() =>
this.functions.processCollectedNestedOptimizedFunctions(environmentRecordIdAfterGlobalCode) this.functions.processCollectedNestedOptimizedFunctions(environmentRecordIdAfterGlobalCode)
); );

View File

@ -34,6 +34,7 @@ export class SerializerStatistics extends RealmStatistics {
this.referenceCounts = new PerformanceTracker(getTime, getMemory); this.referenceCounts = new PerformanceTracker(getTime, getMemory);
this.serializePass = new PerformanceTracker(getTime, getMemory); this.serializePass = new PerformanceTracker(getTime, getMemory);
this.babelGenerate = new PerformanceTracker(getTime, getMemory); this.babelGenerate = new PerformanceTracker(getTime, getMemory);
this.dumpIR = new PerformanceTracker(getTime, getMemory);
} }
resetBeforePass(): void { resetBeforePass(): void {
@ -100,6 +101,7 @@ export class SerializerStatistics extends RealmStatistics {
referenceCounts: PerformanceTracker; referenceCounts: PerformanceTracker;
serializePass: PerformanceTracker; serializePass: PerformanceTracker;
babelGenerate: PerformanceTracker; babelGenerate: PerformanceTracker;
dumpIR: PerformanceTracker;
log(): void { log(): void {
super.log(); super.log();
@ -136,7 +138,7 @@ export class SerializerStatistics extends RealmStatistics {
this.optimizeReactComponentTreeRoots this.optimizeReactComponentTreeRoots
)} optimizing react component tree roots, ${format( )} optimizing react component tree roots, ${format(
this.checkThatFunctionsAreIndependent this.checkThatFunctionsAreIndependent
)} evaluating functions to optimize` )} evaluating functions to optimize, ${format(this.dumpIR)} dumping IR`
); );
console.log( console.log(
`${format(this.deepTraversal)} visiting residual heap, ${format( `${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 { Binding } from "../environment.js";
import type { BabelNodeSourceLocation } from "@babel/types"; import type { BabelNodeSourceLocation } from "@babel/types";
import { optionalStringOfLocation } from "../utils/babelhelpers.js"; 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. * 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 func: Function
): void { ): void {
if (descriptor !== undefined) { if (descriptor !== undefined) {
invariant(descriptor instanceof PropertyDescriptor); // TODO: Handle joined descriptors.
if (descriptor.value !== undefined) { if (descriptor.value !== undefined) {
func(propertyNameOrSymbol, descriptor.value, "value"); func(propertyNameOrSymbol, descriptor.value, "value");
} else { } else {
@ -142,8 +144,17 @@ export function withDescriptorValue(
export const ClassPropertiesToIgnore: Set<string> = new Set(["arguments", "name", "caller"]); export const ClassPropertiesToIgnore: Set<string> = new Set(["arguments", "name", "caller"]);
export function canIgnoreClassLengthProperty(val: ObjectValue, desc: void | Descriptor, logger: Logger): boolean { export function canIgnoreClassLengthProperty(val: ObjectValue, desc: void | Descriptor, logger: Logger): boolean {
if (desc && desc.value === undefined) { if (desc) {
logger.logError(val, "Functions with length accessor properties are not supported in residual heap."); 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; return true;
} }
@ -170,6 +181,11 @@ export function getObjectPrototypeMetadata(
if (_constructor.descriptor === undefined) { if (_constructor.descriptor === undefined) {
throw new FatalError("TODO #1024: implement object prototype serialization with deleted constructor"); 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; let classFunc = _constructor.descriptor.value;
if (classFunc instanceof ECMAScriptSourceFunctionValue) { if (classFunc instanceof ECMAScriptSourceFunctionValue) {
constructor = classFunc; constructor = classFunc;

View File

@ -29,6 +29,7 @@ import type {
} from "./values/index.js"; } from "./values/index.js";
import { Value } from "./values/index.js"; import { Value } from "./values/index.js";
import { Completion } from "./completions.js"; import { Completion } from "./completions.js";
import type { Descriptor as DescriptorClass } from "./descriptors.js";
import { EnvironmentRecord, LexicalEnvironment, Reference } from "./environment.js"; import { EnvironmentRecord, LexicalEnvironment, Reference } from "./environment.js";
import { ObjectValue } from "./values/index.js"; import { ObjectValue } from "./values/index.js";
import type { import type {
@ -134,26 +135,7 @@ export type DataBlock = Uint8Array;
// //
export type Descriptor = { export type Descriptor = DescriptorClass;
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 FunctionBodyAstNode = { export type FunctionBodyAstNode = {
// Function body ast node will have uniqueOrderedTag after being interpreted. // Function body ast node will have uniqueOrderedTag after being interpreted.
@ -399,6 +381,10 @@ export class PathConditions {
return 0; return 0;
} }
getAssumedConditions(): Set<AbstractValue> {
return new Set();
}
refineBaseConditons(realm: Realm, depth?: number = 0): void {} refineBaseConditons(realm: Realm, depth?: number = 0): void {}
} }
@ -478,9 +464,7 @@ export type PropertiesType = {
// ECMA262 13.7.5.15 // ECMA262 13.7.5.15
EnumerateObjectProperties(realm: Realm, O: ObjectValue): ObjectValue, EnumerateObjectProperties(realm: Realm, O: ObjectValue): ObjectValue,
ThrowIfMightHaveBeenDeleted( ThrowIfMightHaveBeenDeleted(desc: Descriptor): void,
value: void | Value | Array<Value> | Array<{ $Key: void | Value, $Value: void | Value }>
): void,
ThrowIfInternalSlotNotWritable<T: ObjectValue>(realm: Realm, object: T, key: string): T, ThrowIfInternalSlotNotWritable<T: ObjectValue>(realm: Realm, object: T, key: string): T,

View File

@ -29,9 +29,36 @@ import {
import { To } from "../singletons.js"; import { To } from "../singletons.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { Logger } from "./logger.js"; import { Logger } from "./logger.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type TargetIntegrityCommand = "freeze" | "seal" | "preventExtensions" | ""; 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 { export class HeapInspector {
constructor(realm: Realm, logger: Logger) { constructor(realm: Realm, logger: Logger) {
this.realm = realm; this.realm = realm;
@ -65,8 +92,8 @@ export class HeapInspector {
for (let propertyBinding of val.properties.values()) { for (let propertyBinding of val.properties.values()) {
let desc = propertyBinding.descriptor; let desc = propertyBinding.descriptor;
if (desc === undefined) continue; //deleted if (desc === undefined) continue; //deleted
if (desc.configurable) anyConfigurable = true; if (hasAnyConfigurable(desc)) anyConfigurable = true;
else if (desc.value !== undefined && desc.writable) anyWritable = true; else if (hasAnyWritable(desc)) anyWritable = true;
} }
command = anyConfigurable ? "preventExtensions" : anyWritable ? "seal" : "freeze"; command = anyConfigurable ? "preventExtensions" : anyWritable ? "seal" : "freeze";
} }
@ -135,6 +162,12 @@ export class HeapInspector {
} }
_canIgnoreProperty(val: ObjectValue, key: string, desc: Descriptor): boolean { _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); let targetDescriptor = this.getTargetIntegrityDescriptor(val);
if (IsArray(this.realm, val)) { if (IsArray(this.realm, val)) {
@ -242,6 +275,7 @@ export class HeapInspector {
if (prototypeBinding === undefined) return undefined; if (prototypeBinding === undefined) return undefined;
let prototypeDesc = prototypeBinding.descriptor; let prototypeDesc = prototypeBinding.descriptor;
if (prototypeDesc === undefined) return undefined; if (prototypeDesc === undefined) return undefined;
invariant(prototypeDesc instanceof PropertyDescriptor);
invariant(prototypeDesc.value === undefined || prototypeDesc.value instanceof Value); invariant(prototypeDesc.value === undefined || prototypeDesc.value instanceof Value);
return prototypeDesc.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 { SerializerOptions } from "../options.js";
import type { PathConditions, ShapeInformationInterface } from "../types.js"; import type { PathConditions, ShapeInformationInterface } from "../types.js";
import { PreludeGenerator } from "./PreludeGenerator.js"; import { PreludeGenerator } from "./PreludeGenerator.js";
import { PropertyDescriptor } from "../descriptors.js";
export type OperationDescriptorType = export type OperationDescriptorType =
| "ABSTRACT_FROM_TEMPLATE" | "ABSTRACT_FROM_TEMPLATE"
@ -147,7 +148,7 @@ export type OperationDescriptorData = {
boundName?: BabelNodeIdentifier, // used by FOR_IN boundName?: BabelNodeIdentifier, // used by FOR_IN
callFunctionRef?: string, // used by EMIT_CALL and EMIT_CALL_AND_CAPTURE_RESULT callFunctionRef?: string, // used by EMIT_CALL and EMIT_CALL_AND_CAPTURE_RESULT
concreteComparisons?: Array<Value>, // used by FULL_INVARIANT_ABSTRACT 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 generator?: Generator, // used by DO_WHILE
generators?: Array<Generator>, // used by JOIN_GENERATORS generators?: Array<Generator>, // used by JOIN_GENERATORS
id?: string, // used by IDENTIFIER id?: string, // used by IDENTIFIER
@ -225,6 +226,19 @@ export type VisitEntryCallbacks = {|
visitBindingAssignment: (Binding, Value) => Value, 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 { export class GeneratorEntry {
constructor(realm: Realm) { constructor(realm: Realm) {
// We increment the index of every TemporalOperationEntry created. // We increment the index of every TemporalOperationEntry created.
@ -236,6 +250,10 @@ export class GeneratorEntry {
this.index = realm.temporalEntryCounter++; this.index = realm.temporalEntryCounter++;
} }
print(printer: Printer): void {
invariant(false, "GeneratorEntry is an abstract base class");
}
visit(callbacks: VisitEntryCallbacks, containingGenerator: Generator): boolean { visit(callbacks: VisitEntryCallbacks, containingGenerator: Generator): boolean {
invariant(false, "GeneratorEntry is an abstract base class"); invariant(false, "GeneratorEntry is an abstract base class");
} }
@ -263,7 +281,6 @@ export type TemporalOperationEntryArgs = {
declared?: AbstractValue | ObjectValue, declared?: AbstractValue | ObjectValue,
args: Array<Value>, args: Array<Value>,
operationDescriptor: OperationDescriptor, operationDescriptor: OperationDescriptor,
dependencies?: Array<Generator>,
isPure?: boolean, isPure?: boolean,
mutatesOnly?: Array<Value>, mutatesOnly?: Array<Value>,
}; };
@ -284,10 +301,17 @@ export class TemporalOperationEntry extends GeneratorEntry {
declared: void | AbstractValue | ObjectValue; declared: void | AbstractValue | ObjectValue;
args: Array<Value>; args: Array<Value>;
operationDescriptor: OperationDescriptor; operationDescriptor: OperationDescriptor;
dependencies: void | Array<Generator>;
isPure: void | boolean; isPure: void | boolean;
mutatesOnly: void | Array<Value>; 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 { toDisplayJson(depth: number): DisplayResult {
if (depth <= 0) return `TemporalOperation${this.index}`; if (depth <= 0) return `TemporalOperation${this.index}`;
let obj = { type: "TemporalOperation", ...this }; let obj = { type: "TemporalOperation", ...this };
@ -345,8 +369,9 @@ export class TemporalOperationEntry extends GeneratorEntry {
} }
} }
} }
if (this.dependencies) let dependencies = this.getDependencies();
for (let dependency of this.dependencies) callbacks.visitGenerator(dependency, containingGenerator); if (dependencies !== undefined)
for (let dependency of dependencies) callbacks.visitGenerator(dependency, containingGenerator);
return true; return true;
} }
} }
@ -393,7 +418,19 @@ export class TemporalOperationEntry extends GeneratorEntry {
} }
getDependencies(): void | Array<Generator> { 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; propertyBinding: PropertyBinding;
newDescriptor: void | Descriptor; newDescriptor: void | Descriptor;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"MODIFIED_PROPERTY",
[],
{ descriptor: this.newDescriptor, propertyBinding: this.propertyBinding },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string { toDisplayString(): string {
let propertyKey = this.propertyBinding.key; let propertyKey = this.propertyBinding.key;
let propertyKeyString = propertyKey instanceof Value ? propertyKey.toDisplayString() : propertyKey; let propertyKeyString = propertyKey instanceof Value ? propertyKey.toDisplayString() : propertyKey;
@ -488,6 +535,16 @@ class ModifiedBindingEntry extends GeneratorEntry {
containingGenerator: Generator; containingGenerator: Generator;
modifiedBinding: Binding; modifiedBinding: Binding;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"MODIFIED_BINDING",
[],
{ binding: this.modifiedBinding, value: this.modifiedBinding.value },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string { toDisplayString(): string {
return `[ModifiedBinding ${this.modifiedBinding.name}]`; return `[ModifiedBinding ${this.modifiedBinding.name}]`;
} }
@ -520,6 +577,10 @@ class ReturnValueEntry extends GeneratorEntry {
returnValue: Value; returnValue: Value;
containingGenerator: Generator; containingGenerator: Generator;
print(printer: Printer): void {
printer.printGeneratorEntry(undefined, "RETURN", [this.returnValue], {}, { isPure: false, mutatesOnly: undefined });
}
toDisplayString(): string { toDisplayString(): string {
return `[Return ${this.returnValue.toDisplayString()}]`; return `[Return ${this.returnValue.toDisplayString()}]`;
} }
@ -552,6 +613,16 @@ class BindingAssignmentEntry extends GeneratorEntry {
binding: Binding; binding: Binding;
value: Value; value: Value;
print(printer: Printer): void {
printer.printGeneratorEntry(
undefined,
"BINDING_ASSIGNMENT",
[this.value],
{ binding: this.binding },
{ isPure: false, mutatesOnly: undefined }
);
}
toDisplayString(): string { toDisplayString(): string {
return `[BindingAssignment ${this.binding.name} = ${this.value.toDisplayString()}]`; return `[BindingAssignment ${this.binding.name} = ${this.value.toDisplayString()}]`;
} }
@ -592,6 +663,10 @@ export class Generator {
_name: string; _name: string;
pathConditions: PathConditions; pathConditions: PathConditions;
print(printer: Printer): void {
for (let entry of this._entries) entry.print(printer);
}
toDisplayString(): string { toDisplayString(): string {
return Utils.jsonToDisplayString(this, 2); return Utils.jsonToDisplayString(this, 2);
} }
@ -664,16 +739,16 @@ export class Generator {
emitPropertyModification(propertyBinding: PropertyBinding): void { emitPropertyModification(propertyBinding: PropertyBinding): void {
invariant(this.effectsToApply !== undefined); invariant(this.effectsToApply !== undefined);
let desc = propertyBinding.descriptor; let desc = propertyBinding.descriptor;
if (desc !== undefined) { if (desc !== undefined && desc instanceof PropertyDescriptor) {
let value = desc.value; let value = desc.value;
if (value instanceof AbstractValue) { if (value instanceof AbstractValue) {
if (value.kind === "conditional") { if (value.kind === "conditional") {
let [c, x, y] = value.args; let [c, x, y] = value.args;
if (c instanceof AbstractValue && c.kind === "template for property name condition") { 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 }); let yprop = Object.assign({}, propertyBinding, { descriptor: ydesc });
this.emitPropertyModification(yprop); 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]; let key = c.args[0];
invariant(key instanceof AbstractValue); invariant(key instanceof AbstractValue);
let xprop = Object.assign({}, propertyBinding, { key, descriptor: xdesc }); 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 (object.refuseSerialization) return;
if (desc.enumerable && desc.configurable && desc.writable && desc.value && !isDescChanged) { if (desc.enumerable && desc.configurable && desc.writable && desc.value && !isDescChanged) {
let descValue = desc.value; let descValue = desc.value;
invariant(descValue instanceof Value); invariant(descValue instanceof Value);
this.emitPropertyAssignment(object, key, descValue); this.emitPropertyAssignment(object, key, descValue);
} else { } else {
desc = Object.assign({}, desc); desc = new PropertyDescriptor(desc);
let descValue = desc.value || object.$Realm.intrinsics.undefined; let descValue = desc.value || object.$Realm.intrinsics.undefined;
invariant(descValue instanceof Value); invariant(descValue instanceof Value);
this._addEntry({ this._addEntry({
@ -777,7 +852,7 @@ export class Generator {
desc.get || object.$Realm.intrinsics.undefined, desc.get || object.$Realm.intrinsics.undefined,
desc.set || 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({ this._addEntry({
args: [], args: [],
operationDescriptor: createOperationDescriptor("DO_WHILE", { generator: body, value: test }), operationDescriptor: createOperationDescriptor("DO_WHILE", { generator: body, value: test }),
dependencies: [body],
}); });
} }
@ -1137,7 +1211,6 @@ export class Generator {
this._addEntry({ this._addEntry({
args: [joinCondition], args: [joinCondition],
operationDescriptor: createOperationDescriptor("JOIN_GENERATORS", { generators }), 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 { HeapInspector } from "../utils/HeapInspector.js";
import { Logger } from "../utils/logger.js"; import { Logger } from "../utils/logger.js";
import { isReactElement } from "../react/utils.js"; import { isReactElement } from "../react/utils.js";
import { PropertyDescriptor, AbstractJoinedDescriptor } from "../descriptors.js";
type LeakedFunctionInfo = { type LeakedFunctionInfo = {
unboundReads: Set<string>, 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 it indeed means deleted binding, should we initialize descriptor with a deleted value?
if (generator !== undefined) generator.emitPropertyDelete(object, name); if (generator !== undefined) generator.emitPropertyDelete(object, name);
} else { } else {
invariant(descriptor instanceof PropertyDescriptor); // TODO: Deal with joined descriptors.
let value = descriptor.value; let value = descriptor.value;
invariant( invariant(
value === undefined || value instanceof Value, value === undefined || value instanceof Value,
@ -216,11 +218,7 @@ class ObjectValueLeakingVisitor {
// inject properties with computed names // inject properties with computed names
if (obj.unknownProperty !== undefined) { if (obj.unknownProperty !== undefined) {
let desc = obj.unknownProperty.descriptor; let desc = obj.unknownProperty.descriptor;
if (desc !== undefined) { this.visitObjectPropertiesWithComputedNamesDescriptor(desc);
let val = desc.value;
invariant(val instanceof AbstractValue);
this.visitObjectPropertiesWithComputedNames(val);
}
} }
// prototype // prototype
@ -258,6 +256,22 @@ class ObjectValueLeakingVisitor {
this.visitValue(proto); 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 { visitObjectPropertiesWithComputedNames(absVal: AbstractValue): void {
if (absVal.kind === "widened property") return; if (absVal.kind === "widened property") return;
if (absVal.kind === "template for prototype member expression") return; if (absVal.kind === "template for prototype member expression") return;
@ -289,11 +303,19 @@ class ObjectValueLeakingVisitor {
} }
} }
visitDescriptor(desc: Descriptor): void { visitDescriptor(desc: void | Descriptor): void {
invariant(desc.value === undefined || desc.value instanceof Value); if (desc === undefined) {
if (desc.value !== undefined) this.visitValue(desc.value); } else if (desc instanceof PropertyDescriptor) {
if (desc.get !== undefined) this.visitValue(desc.get); if (desc.value !== undefined) this.visitValue(desc.value);
if (desc.set !== undefined) this.visitValue(desc.set); 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( visitDeclarativeEnvironmentRecordBinding(

View File

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

View File

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

View File

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

View File

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

View File

@ -25,12 +25,13 @@ import {
Value, Value,
} from "./index.js"; } from "./index.js";
import { TypesDomain, ValuesDomain } from "../domains/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 { Leak, Join, Widen } from "../singletons.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { createOperationDescriptor, type OperationDescriptor } from "../utils/generator.js"; import { createOperationDescriptor, type OperationDescriptor } from "../utils/generator.js";
import { construct_empty_effects } from "../realm.js"; import { construct_empty_effects } from "../realm.js";
import { SimpleNormalCompletion } from "../completions.js"; import { SimpleNormalCompletion } from "../completions.js";
import { cloneDescriptor, equalDescriptors, PropertyDescriptor } from "../descriptors.js";
export default class AbstractObjectValue extends AbstractValue { export default class AbstractObjectValue extends AbstractValue {
constructor( constructor(
@ -326,11 +327,17 @@ export default class AbstractObjectValue extends AbstractValue {
invariant(ob2 instanceof ObjectValue); invariant(ob2 instanceof ObjectValue);
let d1 = ob1.$GetOwnProperty(P); let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$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 // We do not handle the case where different loop iterations result in different kinds of propperties
AbstractValue.reportIntrospectionError(this, P); AbstractValue.reportIntrospectionError(this, P);
throw new FatalError(); 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); let desc = cloneDescriptor(d1);
invariant(desc !== undefined); invariant(desc !== undefined);
if (IsDataDescriptor(this.$Realm, desc)) { if (IsDataDescriptor(this.$Realm, desc)) {
@ -340,7 +347,9 @@ export default class AbstractObjectValue extends AbstractValue {
invariant(d1Value instanceof Value); invariant(d1Value instanceof Value);
let d2Value = d2.value; let d2Value = d2.value;
invariant(d2Value instanceof 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 { } else {
// In this case equalDescriptors guarantees exact equality betwee d1 and d2. // 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 // 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 // ECMA262 9.1.6
$DefineOwnProperty(_P: PropertyKeyValue, Desc: Descriptor): boolean { $DefineOwnProperty(_P: PropertyKeyValue, _Desc: Descriptor): boolean {
let P = _P; let P = _P;
if (P instanceof StringValue) P = P.value; if (P instanceof StringValue) P = P.value;
if (this.values.isTop()) { if (this.values.isTop()) {
@ -378,10 +387,11 @@ export default class AbstractObjectValue extends AbstractValue {
if (elements.size === 1) { if (elements.size === 1) {
for (let cv of elements) { for (let cv of elements) {
invariant(cv instanceof ObjectValue); invariant(cv instanceof ObjectValue);
return cv.$DefineOwnProperty(P, Desc); return cv.$DefineOwnProperty(P, _Desc);
} }
invariant(false); invariant(false);
} else { } else {
let Desc = _Desc.throwIfNotConcrete(this.$Realm);
if (!IsDataDescriptor(this.$Realm, Desc)) { if (!IsDataDescriptor(this.$Realm, Desc)) {
AbstractValue.reportIntrospectionError(this, P); AbstractValue.reportIntrospectionError(this, P);
throw new FatalError(); throw new FatalError();
@ -395,13 +405,21 @@ export default class AbstractObjectValue extends AbstractValue {
break; break;
} }
} }
let desc = { if (firstExistingDesc) {
value: "value" in Desc ? Desc.value : this.$Realm.intrinsics.undefined, firstExistingDesc = firstExistingDesc.throwIfNotConcrete(this.$Realm);
writable: "writable" in Desc ? Desc.writable : firstExistingDesc ? firstExistingDesc.writable : false, }
enumerable: "enumerable" in Desc ? Desc.enumerable : firstExistingDesc ? firstExistingDesc.enumerable : false, 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:
"configurable" in Desc ? Desc.configurable : firstExistingDesc ? firstExistingDesc.configurable : false, Desc.configurable !== undefined
}; ? Desc.configurable
: firstExistingDesc
? firstExistingDesc.configurable
: false,
});
let newVal = desc.value; let newVal = desc.value;
if (this.kind === "conditional") { if (this.kind === "conditional") {
// this is the join of two concrete/abstract objects // 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); invariant(ob2 instanceof ObjectValue || ob2 instanceof AbstractObjectValue);
let d1 = ob1.$GetOwnProperty(P); let d1 = ob1.$GetOwnProperty(P);
let d2 = ob2.$GetOwnProperty(P); let d2 = ob2.$GetOwnProperty(P);
if ((d1 !== undefined && !equalDescriptors(d1, desc)) || (d2 !== undefined && !equalDescriptors(d2, desc))) { if (d1 !== undefined) {
AbstractValue.reportIntrospectionError(this, P); d1 = d1.throwIfNotConcrete(this.$Realm);
throw new FatalError(); 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 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; 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) { for (let cv of elements) {
invariant(cv instanceof ObjectValue); invariant(cv instanceof ObjectValue);
let d = cv.$GetOwnProperty(P); let d = cv.$GetOwnProperty(P);
if (d !== undefined && !equalDescriptors(d, desc)) { if (d !== undefined) {
AbstractValue.reportIntrospectionError(this, P); d = d.throwIfNotConcrete(this.$Realm);
throw new FatalError(); if (!equalDescriptors(d, desc)) {
AbstractValue.reportIntrospectionError(this, P);
throw new FatalError();
}
} }
let dval = d === undefined || d.value === undefined ? this.$Realm.intrinsics.empty : d.value; let dval = d === undefined || d.value === undefined ? this.$Realm.intrinsics.empty : d.value;
invariant(dval instanceof Value); invariant(dval instanceof Value);
@ -957,14 +988,18 @@ export default class AbstractObjectValue extends AbstractValue {
let result1 = true; let result1 = true;
let result2 = true; let result2 = true;
if (d1 !== undefined) { if (d1 !== undefined) {
d1 = d1.throwIfNotConcrete(this.$Realm);
let newDesc1 = cloneDescriptor(d1); let newDesc1 = cloneDescriptor(d1);
invariant(newDesc1); invariant(newDesc1);
newDesc1 = newDesc1.throwIfNotConcrete(this.$Realm);
newDesc1.value = newVal1; newDesc1.value = newVal1;
result1 = ob1.$DefineOwnProperty(P, newDesc1); result1 = ob1.$DefineOwnProperty(P, newDesc1);
} }
if (d2 !== undefined) { if (d2 !== undefined) {
d2 = d2.throwIfNotConcrete(this.$Realm);
let newDesc2 = cloneDescriptor(d2); let newDesc2 = cloneDescriptor(d2);
invariant(newDesc2); invariant(newDesc2);
newDesc2 = newDesc2.throwIfNotConcrete(this.$Realm);
newDesc2.value = newVal2; newDesc2.value = newVal2;
result2 = ob2.$DefineOwnProperty(P, newDesc2); 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 { Get, OrdinaryGet } from "../methods/get.js";
import { Properties } from "../singletons.js"; import { Properties } from "../singletons.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { PropertyDescriptor } from "../descriptors.js";
export default class ArgumentsExotic extends ObjectValue { export default class ArgumentsExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) { constructor(realm: Realm, intrinsicName?: string) {
@ -36,7 +37,8 @@ export default class ArgumentsExotic extends ObjectValue {
// 3. If desc is undefined, return desc. // 3. If desc is undefined, return desc.
if (desc === undefined) return undefined; if (desc === undefined) return undefined;
Properties.ThrowIfMightHaveBeenDeleted(desc.value); Properties.ThrowIfMightHaveBeenDeleted(desc);
desc = desc.throwIfNotConcrete(this.$Realm);
// 4. Let map be args.[[ParameterMap]]. // 4. Let map be args.[[ParameterMap]].
let map = args.$ParameterMap; let map = args.$ParameterMap;
@ -56,7 +58,9 @@ export default class ArgumentsExotic extends ObjectValue {
} }
// ECMA262 9.4.4.2 // 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. // 1. Let args be the arguments object.
let args = this; 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 // 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) { if (Desc.value === undefined && Desc.writable === false) {
// i. Let newArgDesc be a copy of Desc. // i. Let newArgDesc be a copy of Desc.
newArgDesc = Object.assign({}, Desc); newArgDesc = new PropertyDescriptor(Desc);
// ii. Set newArgDesc.[[Value]] to Get(map, P). // ii. Set newArgDesc.[[Value]] to Get(map, P).
newArgDesc.value = Get(this.$Realm, 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 { type OperationDescriptor } from "../utils/generator.js";
import invariant from "../invariant.js"; import invariant from "../invariant.js";
import { NestedOptimizedFunctionSideEffect } from "../errors.js"; import { NestedOptimizedFunctionSideEffect } from "../errors.js";
import { PropertyDescriptor } from "../descriptors.js";
type PossibleNestedOptimizedFunctions = [ type PossibleNestedOptimizedFunctions = [
{ func: BoundFunctionValue | ECMAScriptSourceFunctionValue, thisValue: Value }, { func: BoundFunctionValue | ECMAScriptSourceFunctionValue, thisValue: Value },
@ -98,9 +99,9 @@ function createArrayWithWidenedNumericProperty(
// Add unknownProperty so we manually handle this object property access // Add unknownProperty so we manually handle this object property access
abstractArrayValue.unknownProperty = { abstractArrayValue.unknownProperty = {
key: undefined, key: undefined,
descriptor: { descriptor: new PropertyDescriptor({
value: AbstractValue.createFromType(realm, Value, "widened numeric property"), value: AbstractValue.createFromType(realm, Value, "widened numeric property"),
}, }),
object: abstractArrayValue, object: abstractArrayValue,
}; };
return abstractArrayValue; return abstractArrayValue;
@ -149,7 +150,8 @@ export default class ArrayValue extends ObjectValue {
oldLenDesc !== undefined && !IsAccessorDescriptor(this.$Realm, oldLenDesc), oldLenDesc !== undefined && !IsAccessorDescriptor(this.$Realm, oldLenDesc),
"cannot be undefined or an accessor descriptor" "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]]. // c. Let oldLen be oldLenDesc.[[Value]].
let oldLen = oldLenDesc.value; let oldLen = oldLenDesc.value;
@ -211,8 +213,7 @@ export default class ArrayValue extends ObjectValue {
if (obj instanceof ArrayValue && obj.intrinsicName) { if (obj instanceof ArrayValue && obj.intrinsicName) {
const prop = obj.unknownProperty; const prop = obj.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined) { 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"; 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); invariant(binding);
let desc = binding.descriptor; let desc = binding.descriptor;
invariant(desc); invariant(desc);
let value = desc.value; let value = desc.throwIfNotConcrete(this.$Realm).value;
if (!(value instanceof NumberValue)) return undefined; if (!(value instanceof NumberValue)) return undefined;
return value.value; return value.value;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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