Re-design of the buildNode logic for AbstractValue and GeneratorEntries (#2296)

Summary:
Release notes: much of the buildNode and inline Babel logic has been moved to a dedicated ResidualOperationSerializer class

Note: this PR is like *phase 1* of many phases to move around buildNodes and their logic. Furthermore, the ideas is to move all Babel coupled logic to the serialization phase and away from the generator and other generic areas. I've explained this more in detail below.

This PR moves much of the Babel logic involving build nodes, that was coupled to inline closures through many parts of the Prepack code base, to its own class `ResidualOperationSerializer`. We now use a helper function `createOperationDescriptor` to create `OperationDescriptor`s. `OperationDescriptor`s exist on both `AbstractValue` and `GeneratorEntry`, and replaces the old `buildNode` and `_buildNode` properties and unifies all the logic. The `buildNode` field has now changed to `operationDescriptor` respectfully.

We now have a `OperationDescriptorType` enum that tells the class which build node to use, along with some associated data that is needed to construct the build node (previously this data was captured in the closure). The idea is to remove the data/kind arguments from `createOperationDescriptor` in follow up PRs, but that will take quite a bit more work, thus why this is a phase 1 PR.

In many cases, we now have no instances of importing Babel in many of the files and this unifies much (but not all) of the Babel node creation logic into a single file – allowing for easy extension in the future.

This PR took forever to do and is very large, so there might be bits that I missed, but hopefully I haven't. All tests etc pass locally.
Pull Request resolved: https://github.com/facebook/prepack/pull/2296

Differential Revision: D8923889

Pulled By: trueadm

fbshipit-source-id: aa3a27efd97f10cc941159735d598199deb24f52
This commit is contained in:
Dominic Gannaway 2018-07-19 15:34:29 -07:00 committed by Facebook Github Bot
parent 108dea533b
commit bf0ed59174
60 changed files with 1747 additions and 779 deletions

View File

@ -20,10 +20,11 @@ import type {
} from "@babel/types";
import type { Realm } from "./realm.js";
import type { SourceFile, SourceMap, SourceType } from "./types.js";
import * as t from "@babel/types";
import { AbruptCompletion, Completion, ThrowCompletion } from "./completions.js";
import { CompilerDiagnostic, FatalError } from "./errors.js";
import { defaultOptions } from "./options";
import { defaultOptions } from "./options.js";
import type { PartialEvaluatorOptions } from "./options";
import { ExecutionContext } from "./realm.js";
import {
@ -46,9 +47,9 @@ import invariant from "./invariant.js";
import traverseFast from "./utils/traverse-fast.js";
import { HasProperty, Get, IsExtensible, HasOwnProperty, IsDataDescriptor } from "./methods/index.js";
import { Environment, Havoc, Properties, To } from "./singletons.js";
import * as t from "@babel/types";
import { TypesDomain, ValuesDomain } from "./domains/index.js";
import PrimitiveValue from "./values/PrimitiveValue";
import PrimitiveValue from "./values/PrimitiveValue.js";
import { createOperationDescriptor } from "./utils/generator.js";
const sourceMap = require("source-map");
@ -56,7 +57,7 @@ function deriveGetBinding(realm: Realm, binding: Binding) {
let types = TypesDomain.topVal;
let values = ValuesDomain.topVal;
invariant(realm.generator !== undefined);
return realm.generator.deriveAbstract(types, values, [], (_, context) => context.serializeBinding(binding));
return realm.generator.deriveAbstract(types, values, [], createOperationDescriptor("GET_BINDING", { binding }));
}
export function havocBinding(binding: Binding): void {

View File

@ -27,13 +27,8 @@ import {
} from "../values/index.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { Environment, Havoc, To } from "../singletons.js";
import type {
BabelBinaryOperator,
BabelNodeBinaryExpression,
BabelNodeExpression,
BabelNodeSourceLocation,
} from "@babel/types";
import * as t from "@babel/types";
import type { BabelBinaryOperator, BabelNodeBinaryExpression, BabelNodeSourceLocation } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
import invariant from "../invariant.js";
export default function(
@ -251,7 +246,7 @@ export function computeBinary(
realm,
resultType,
[lval, rval],
([lnode, rnode]: Array<BabelNodeExpression>) => t.binaryExpression(op, lnode, rnode)
createOperationDescriptor("BINARY_EXPRESSION", { op })
),
TypesDomain.topVal,
ValuesDomain.topVal

View File

@ -34,11 +34,10 @@ import {
IsInTailPosition,
SameValue,
} from "../methods/index.js";
import type { BabelNodeCallExpression, BabelNodeExpression, BabelNodeSpreadElement } from "@babel/types";
import type { BabelNodeCallExpression } from "@babel/types";
import invariant from "../invariant.js";
import * as t from "@babel/types";
import SuperCall from "./SuperCall";
import { memberExpressionHelper } from "../utils/babelhelpers.js";
import SuperCall from "./SuperCall.js";
import { createOperationDescriptor } from "../utils/generator.js";
export default function(
ast: BabelNodeCallExpression,
@ -235,22 +234,12 @@ function generateRuntimeCall(
}
}
let resultType = (func instanceof AbstractObjectValue ? func.functionResultType : undefined) || Value;
return AbstractValue.createTemporalFromBuildFunction(realm, resultType, args, nodes => {
let callFunc;
let argStart = 1;
if (thisArg instanceof Value) {
if (typeof propName === "string") {
callFunc = memberExpressionHelper(nodes[0], propName);
} else {
callFunc = memberExpressionHelper(nodes[0], nodes[1]);
argStart = 2;
}
} else {
callFunc = nodes[0];
}
let fun_args = ((nodes.slice(argStart): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(callFunc, fun_args);
});
return AbstractValue.createTemporalFromBuildFunction(
realm,
resultType,
args,
createOperationDescriptor("CALL_BAILOUT", { propRef: propName, thisArg })
);
}
function tryToEvaluateCallOrLeaveAsAbstract(

View File

@ -40,6 +40,7 @@ import invariant from "../invariant.js";
import * as t from "@babel/types";
import type { FunctionBodyAstNode } from "../types.js";
import type { BabelNodeExpression, BabelNodeForStatement, BabelNodeBlockStatement } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
type BailOutWrapperInfo = {
usesArguments: boolean,
@ -455,10 +456,7 @@ function generateRuntimeForStatement(
realm,
Value,
args,
([func, thisExpr]) =>
usesThis
? t.callExpression(t.memberExpression(func, t.identifier("call")), [thisExpr])
: t.callExpression(func, [])
createOperationDescriptor("FOR_STATEMENT_FUNC", { usesThis })
);
invariant(wrapperValue instanceof AbstractValue);
return wrapperValue;

View File

@ -19,8 +19,8 @@ import { IsConstructor, ArgumentListEvaluation } from "../methods/index.js";
import { Construct } from "../methods/index.js";
import invariant from "../invariant.js";
import { FatalError } from "../errors.js";
import * as t from "@babel/types";
import { BabelNodeNewExpression, type BabelNodeExpression } from "@babel/types";
import { BabelNodeNewExpression } from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
export default function(
ast: BabelNodeNewExpression,
@ -102,8 +102,7 @@ function tryToEvaluateConstructOrLeaveAsAbstract(
realm,
ObjectValue,
[constructor, ...argsList],
([constructorNode, ...argListNodes]: Array<BabelNodeExpression>) =>
t.newExpression(constructorNode, argListNodes)
createOperationDescriptor("NEW_EXPRESSION")
),
TypesDomain.topVal,
ValuesDomain.topVal

View File

@ -18,8 +18,8 @@ import { AbstractValue, NumberValue, IntegralValue } from "../values/index.js";
import type { BabelNodeUpdateExpression } from "@babel/types";
import { Environment, Havoc, Properties, To } from "../singletons.js";
import invariant from "../invariant.js";
import * as t from "@babel/types";
import { ValuesDomain, TypesDomain } from "../domains/index.js";
import { createOperationDescriptor } from "../utils/generator.js";
export default function(
ast: BabelNodeUpdateExpression,
@ -46,8 +46,11 @@ export default function(
Havoc.value(realm, oldExpr);
newAbstractValue = realm.evaluateWithPossibleThrowCompletion(
() =>
AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, [oldExpr], ([oldValNode]) =>
t.binaryExpression(op, oldValNode, t.numericLiteral(1))
AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
[oldExpr],
createOperationDescriptor("UPDATE_INCREMENTOR", { op })
),
TypesDomain.topVal,
ValuesDomain.topVal

View File

@ -31,10 +31,10 @@ import {
IsConstructor,
IsCallable,
} from "../../methods/index.js";
import * as t from "@babel/types";
import { GetIterator, IteratorClose, IteratorStep, IteratorValue } from "../../methods/iterator.js";
import { Create, Havoc, Properties, To } from "../../singletons.js";
import invariant from "../../invariant.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export default function(realm: Realm): NativeFunctionValue {
let func = new NativeFunctionValue(realm, "Array", "Array", 1, (context, [...items], argCount, NewTarget) => {
@ -247,7 +247,7 @@ export default function(realm: Realm): NativeFunctionValue {
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
args,
([methodNode, ..._args]) => t.callExpression(methodNode, ((_args: any): Array<any>)),
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL"),
{ func: mapfn, thisVal: thisArg }
);
}

View File

@ -15,7 +15,7 @@ import { To } from "../../singletons.js";
import { Get } from "../../methods/get.js";
import { Call } from "../../methods/call.js";
import { IsCallable } from "../../methods/is.js";
import * as t from "@babel/types";
import { createOperationDescriptor } from "../../utils/generator.js";
export default function(realm: Realm): NativeFunctionValue {
// ECMA262 22.1.3.30
@ -36,8 +36,11 @@ export default function(realm: Realm): NativeFunctionValue {
realm.isInPureScope() &&
array.$GetOwnProperty("toString") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, StringValue, [array], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("toString")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
StringValue,
[array],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "toString" })
);
}

View File

@ -12,7 +12,7 @@
import type { Realm } from "../../realm.js";
import { AbstractValue, ArrayValue, NativeFunctionValue, Value } from "../../values/index.js";
import { Create, To } from "../../singletons.js";
import * as t from "@babel/types";
import { createOperationDescriptor } from "../../utils/generator.js";
export default function(realm: Realm): NativeFunctionValue {
// ECMA262 22.1.3.30
@ -28,8 +28,11 @@ export default function(realm: Realm): NativeFunctionValue {
realm.isInPureScope() &&
O.$GetOwnProperty("values") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("values")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "values" })
);
}

View File

@ -23,7 +23,6 @@ import {
Value,
} from "../../values/index.js";
import invariant from "../../invariant.js";
import * as t from "@babel/types";
import { SameValueZeroPartial, AbstractRelationalComparison } from "../../methods/abstract.js";
import {
StrictEqualityComparisonPartial,
@ -38,6 +37,7 @@ import {
HasSomeCompatibleType,
} from "../../methods/index.js";
import { Create, Join, Properties, To } from "../../singletons.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 22.1.3.31
@ -57,8 +57,10 @@ export default function(realm: Realm, obj: ObjectValue): void {
O.$GetOwnProperty("concat") === undefined
) {
let newArgs = [O, ...args];
return ArrayValue.createTemporalWithWidenedNumericProperty(realm, newArgs, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("concat")), ((_args: any): Array<any>))
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
newArgs,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "concat" })
);
}
@ -161,8 +163,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (end) {
args.push(end);
}
AbstractValue.createTemporalFromBuildFunction(realm, BooleanValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("copyWithin")), ((_args: any): Array<any>))
AbstractValue.createTemporalFromBuildFunction(
realm,
BooleanValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "copyWithin" })
);
return O;
}
@ -258,8 +263,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("entries") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("entries")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "entries" })
);
}
@ -284,8 +292,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (thisArg) {
args.push(thisArg);
}
return AbstractValue.createTemporalFromBuildFunction(realm, BooleanValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("every")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
BooleanValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "every" })
);
}
@ -351,8 +362,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (end) {
args.push(end);
}
AbstractValue.createTemporalFromBuildFunction(realm, Value, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("fill")), ((_args: any): Array<any>))
AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "fill" })
);
return O;
}
@ -405,8 +419,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
args,
([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("filter")), ((_args: any): Array<any>)),
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "filter" }),
{ func: callbackfn, thisVal: thisArg }
);
}
@ -482,8 +495,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (thisArg) {
args.push(thisArg);
}
return AbstractValue.createTemporalFromBuildFunction(realm, Value, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("find")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "find" })
);
}
@ -540,8 +556,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (thisArg) {
args.push(thisArg);
}
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("findIndex")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "findIndex" })
);
}
@ -598,8 +617,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (thisArg) {
args.push(thisArg);
}
AbstractValue.createTemporalFromBuildFunction(realm, BooleanValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("forEach")), ((_args: any): Array<any>))
AbstractValue.createTemporalFromBuildFunction(
realm,
BooleanValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "forEach" })
);
return realm.intrinsics.undefined;
}
@ -661,8 +683,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (fromIndex) {
args.push(fromIndex);
}
return AbstractValue.createTemporalFromBuildFunction(realm, BooleanValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("includes")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
BooleanValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "includes" })
);
}
@ -721,8 +746,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (fromIndex) {
args.push(fromIndex);
}
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("indexOf")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "indexOf" })
);
}
@ -794,8 +822,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (separator) {
args.push(separator);
}
return AbstractValue.createTemporalFromBuildFunction(realm, StringValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("join")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
StringValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "join" })
);
}
@ -865,8 +896,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("keys") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("keys")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "keys" })
);
}
@ -891,8 +925,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (fromIndex) {
args.push(fromIndex);
}
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("lastIndexOf")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "lastIndexOf" })
);
}
@ -958,8 +995,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
args,
([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("map")), ((_args: any): Array<any>)),
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "map" }),
{ func: callbackfn, thisVal: thisArg }
);
}
@ -1045,8 +1081,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("pop") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("pop")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "pop" })
);
}
@ -1095,8 +1134,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("push") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, [O, ...args], ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("push")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
[O, ...args],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "push" })
);
}
@ -1150,8 +1192,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (initialValue) {
args.push(initialValue);
}
return AbstractValue.createTemporalFromBuildFunction(realm, Value, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("reduce")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "reduce" })
);
}
@ -1254,8 +1299,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (initialValue) {
args.push(initialValue);
}
return AbstractValue.createTemporalFromBuildFunction(realm, Value, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("reduceRight")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "reduceRight" })
);
}
@ -1352,8 +1400,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("reverse") === undefined
) {
AbstractValue.createTemporalFromBuildFunction(realm, ArrayValue, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("reverse")), [])
AbstractValue.createTemporalFromBuildFunction(
realm,
ArrayValue,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "reverse" })
);
return O;
}
@ -1452,8 +1503,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("shift") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, Value, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("shift")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "shift" })
);
}
@ -1524,8 +1578,10 @@ export default function(realm: Realm, obj: ObjectValue): void {
O.$GetOwnProperty("slice") === undefined
) {
let newArgs = [O, start, end];
return ArrayValue.createTemporalWithWidenedNumericProperty(realm, newArgs, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("slice")), ((_args: any): Array<any>))
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
newArgs,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "slice" })
);
}
@ -1601,8 +1657,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (thisArg) {
args.push(thisArg);
}
return AbstractValue.createTemporalFromBuildFunction(realm, BooleanValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("some")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
BooleanValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "some" })
);
}
@ -1665,8 +1724,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
O.$GetOwnProperty("sort") === undefined
) {
let args = [O, comparefn];
AbstractValue.createTemporalFromBuildFunction(realm, Value, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("sort")), ((_args: any): Array<any>))
AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "sort" })
);
// context is returned instead of O at the end of this method
// so we do the same here
@ -1854,8 +1916,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
if (items && items.length > 0) {
args.push(...items);
}
return AbstractValue.createTemporalFromBuildFunction(realm, ArrayValue, args, ([objNode, ..._args]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("splice")), ((_args: any): Array<any>))
return AbstractValue.createTemporalFromBuildFunction(
realm,
ArrayValue,
args,
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "splice" })
);
}
@ -2052,8 +2117,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
array.$GetOwnProperty("toLocaleString") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, StringValue, [array], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("toLocaleString")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
StringValue,
[array],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "toLocaleString" })
);
}
@ -2129,8 +2197,11 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm.isInPureScope() &&
O.$GetOwnProperty("unshift") === undefined
) {
return AbstractValue.createTemporalFromBuildFunction(realm, NumberValue, [O], ([objNode]) =>
t.callExpression(t.memberExpression(objNode, t.identifier("unshift")), [])
return AbstractValue.createTemporalFromBuildFunction(
realm,
NumberValue,
[O],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_PROPERTY_CALL", { propName: "unshift" })
);
}

View File

@ -38,7 +38,7 @@ import {
HasSomeCompatibleType,
} from "../../methods/index.js";
import { Create, Havoc, Properties as Props, To } from "../../singletons.js";
import * as t from "@babel/types";
import { createOperationDescriptor } from "../../utils/generator.js";
import invariant from "../../invariant.js";
function snapshotToObjectAndRemoveProperties(
@ -381,7 +381,7 @@ export default function(realm: Realm): NativeFunctionValue {
realm,
ObjectValue,
[getOwnPropertyDescriptor, obj, P],
([methodNode, objNode, keyNode]) => t.callExpression(methodNode, [objNode, keyNode])
createOperationDescriptor("OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR")
);
invariant(result instanceof AbstractObjectValue);
result.makeSimple();
@ -500,12 +500,14 @@ export default function(realm: Realm): NativeFunctionValue {
let array = ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectKeys, obj],
([methodNode, objNode]) => t.callExpression(methodNode, [objNode])
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
return array;
} else if (ArrayValue.isIntrinsicAndHasWidenedNumericProperty(obj)) {
return ArrayValue.createTemporalWithWidenedNumericProperty(realm, [objectKeys, obj], ([methodNode, objNode]) =>
t.callExpression(methodNode, [objNode])
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectKeys, obj],
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
}
@ -529,14 +531,14 @@ export default function(realm: Realm): NativeFunctionValue {
let array = ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectValues, obj],
([methodNode, objNode]) => t.callExpression(methodNode, [objNode])
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
return array;
} else if (ArrayValue.isIntrinsicAndHasWidenedNumericProperty(obj)) {
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectValues, obj],
([methodNode, objNode]) => t.callExpression(methodNode, [objNode])
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
}
}
@ -561,14 +563,14 @@ export default function(realm: Realm): NativeFunctionValue {
let array = ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectEntries, obj],
([methodNode, objNode]) => t.callExpression(methodNode, [objNode])
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
return array;
} else if (ArrayValue.isIntrinsicAndHasWidenedNumericProperty(obj)) {
return ArrayValue.createTemporalWithWidenedNumericProperty(
realm,
[objectEntries, obj],
([methodNode, objNode]) => t.callExpression(methodNode, [objNode])
createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL")
);
}

View File

@ -23,10 +23,9 @@ import { HasOwnProperty, HasSomeCompatibleType } from "../../methods/has.js";
import { Invoke } from "../../methods/call.js";
import { Properties, To } from "../../singletons.js";
import { FatalError } from "../../errors.js";
import type { BabelNodeExpression } from "@babel/types";
import * as t from "@babel/types";
import invariant from "../../invariant.js";
import { TypesDomain, ValuesDomain } from "../../domains/index.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export default function(realm: Realm, obj: ObjectValue): void {
// ECMA262 19.1.3.2
@ -54,9 +53,7 @@ export default function(realm: Realm, obj: ObjectValue): void {
realm,
BooleanValue,
[ObjectPrototypeHasOwnPrototype, context, key],
([methodNode, objectNode, nameNode]: Array<BabelNodeExpression>) => {
return t.callExpression(t.memberExpression(methodNode, t.identifier("call")), [objectNode, nameNode]);
}
createOperationDescriptor("OBJECT_PROTO_HAS_OWN_PROPERTY")
),
TypesDomain.topVal,
ValuesDomain.topVal

View File

@ -23,10 +23,10 @@ import {
} from "../../values/index.js";
import { Create } from "../../singletons.js";
import { Get } from "../../methods/index.js";
import * as t from "@babel/types";
import invariant from "../../invariant";
import invariant from "../../invariant.js";
import { Properties } from "../../singletons.js";
import { forEachArrayValue } from "../../react/utils.js";
import { createOperationDescriptor } from "../../utils/generator.js";
const fbMagicGlobalFunctions = [
"asset",
@ -117,9 +117,7 @@ function createBabelHelpers(realm: Realm, global: ObjectValue | AbstractObjectVa
realm,
ObjectValue,
temporalArgs,
([methodNode, objNode, propRemoveNode]) => {
return t.callExpression(methodNode, [objNode, propRemoveNode]);
},
createOperationDescriptor("BABEL_HELPERS_OBJECT_WITHOUT_PROPERTIES"),
temporalConfig
);
invariant(value instanceof AbstractObjectValue);
@ -205,7 +203,7 @@ function createMagicGlobalFunction(realm: Realm, global: ObjectValue | AbstractO
realm,
FunctionValue,
args,
_args => t.callExpression(t.identifier(functionName), ((_args: any): Array<any>)),
createOperationDescriptor("FB_MOCKS_MAGIC_GLOBAL_FUNCTION", { propName: functionName }),
{ skipInvariant: true, isPure: true }
);
invariant(val instanceof AbstractValue);
@ -238,11 +236,7 @@ function createBootloader(realm: Realm, global: ObjectValue | AbstractObjectValu
realm,
FunctionValue,
args,
_args =>
t.callExpression(
t.memberExpression(t.identifier("Bootloader"), t.identifier("loadModules")),
((_args: any): Array<any>)
),
createOperationDescriptor("FB_MOCKS_BOOTLOADER_LOAD_MODULES"),
{ skipInvariant: true }
);
invariant(val instanceof AbstractValue);

View File

@ -12,10 +12,10 @@
import type { Realm } from "../../realm.js";
import { ObjectValue, AbstractObjectValue, AbstractValue, FunctionValue } from "../../values/index.js";
import { createReactHintObject, isReactElement } from "../../react/utils.js";
import * as t from "@babel/types";
import invariant from "../../invariant";
import invariant from "../../invariant.js";
import { updateIntrinsicNames, addMockFunctionToObject } from "./utils.js";
import { renderToString } from "../../react/experimental-server-rendering/rendering.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export function createMockReactDOM(realm: Realm, reactDomRequireName: string): ObjectValue {
let reactDomValue = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
@ -28,9 +28,7 @@ export function createMockReactDOM(realm: Realm, reactDomRequireName: string): O
realm,
FunctionValue,
[funcVal, ...args],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);
@ -47,9 +45,7 @@ export function createMockReactDOM(realm: Realm, reactDomRequireName: string): O
realm,
ObjectValue,
[funcVal, reactPortalValue, domNodeValue],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);
@ -78,9 +74,7 @@ export function createMockReactDOMServer(realm: Realm, requireName: string): Obj
realm,
FunctionValue,
[funcVal, ...args],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(reactDomMethod instanceof AbstractObjectValue);

View File

@ -26,9 +26,9 @@ import { Environment } from "../../singletons.js";
import { createReactHintObject, getReactSymbol } from "../../react/utils.js";
import { cloneReactElement, createReactElement } from "../../react/elements.js";
import { Properties, Create, To } from "../../singletons.js";
import * as t from "@babel/types";
import invariant from "../../invariant";
import invariant from "../../invariant.js";
import { updateIntrinsicNames, addMockFunctionToObject } from "./utils.js";
import { createOperationDescriptor } from "../../utils/generator.js";
// most of the code here was taken from https://github.com/facebook/react/blob/master/packages/react/src/ReactElement.js
let reactCode = `
@ -489,7 +489,7 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
realm,
ObjectValue,
[funcValue, defaultValue],
([methodNode, defaultValueNode]) => t.callExpression(methodNode, [defaultValueNode]),
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(consumer instanceof AbstractObjectValue);
@ -499,7 +499,7 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
realm,
ObjectValue,
[consumer],
([consumerNode]) => t.memberExpression(consumerNode, t.identifier("Provider")),
createOperationDescriptor("REACT_CREATE_CONTEXT_PROVIDER"),
{ skipInvariant: true, isPure: true }
);
invariant(provider instanceof AbstractObjectValue);
@ -524,9 +524,7 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
realm,
FunctionValue,
[funcVal],
([createRefNode]) => {
return t.callExpression(createRefNode, []);
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(createRef instanceof AbstractObjectValue);
@ -538,9 +536,7 @@ export function createMockReact(realm: Realm, reactRequireName: string): ObjectV
realm,
FunctionValue,
[funcVal, func],
([forwardRefNode, funcNode]) => {
return t.callExpression(forwardRefNode, [funcNode]);
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
invariant(forwardedRef instanceof AbstractObjectValue);

View File

@ -12,9 +12,9 @@
import type { Realm } from "../../realm.js";
import { AbstractValue, ECMAScriptSourceFunctionValue, ObjectValue, StringValue } from "../../values/index.js";
import { Environment } from "../../singletons.js";
import invariant from "../../invariant";
import * as t from "@babel/types";
import invariant from "../../invariant.js";
import { parseExpression } from "@babel/parser";
import { createOperationDescriptor } from "../../utils/generator.js";
let reactNativeCode = `
function createReactNative(React, reactNameRequireName) {
@ -1660,7 +1660,7 @@ export function createMockReactNative(realm: Realm, reactNativeRequireName: stri
realm,
StringValue,
[],
() => t.stringLiteral("RCTView"),
createOperationDescriptor("REACT_NATIVE_STRING_LITERAL", { propName: "RCTView" }),
{ skipInvariant: true, isPure: true }
);
invariant(RCTViewDerivedReference instanceof AbstractValue);
@ -1670,7 +1670,7 @@ export function createMockReactNative(realm: Realm, reactNativeRequireName: stri
realm,
StringValue,
[],
() => t.stringLiteral("RCTText"),
createOperationDescriptor("REACT_NATIVE_STRING_LITERAL", { propName: "RCTText" }),
{ skipInvariant: true, isPure: true }
);
invariant(RCTTextDerivedReference instanceof AbstractValue);

View File

@ -14,11 +14,11 @@ import { ObjectValue, FunctionValue, AbstractValue, ECMAScriptSourceFunctionValu
import { Create, Environment } from "../../singletons.js";
import { createAbstract } from "../prepack/utils.js";
import { Get } from "../../methods/index.js";
import * as t from "@babel/types";
import invariant from "../../invariant";
import invariant from "../../invariant.js";
import { createReactHintObject } from "../../react/utils.js";
import { parseExpression } from "@babel/parser";
import { addMockFunctionToObject } from "./utils.js";
import { createOperationDescriptor } from "../../utils/generator.js";
let reactRelayCode = `
function createReactRelay(React) {
@ -113,14 +113,7 @@ function createReactRelayContainer(
realm,
FunctionValue,
[reactRelay, ...args],
_args => {
let [reactRelayIdent, ...otherArgs] = _args;
return t.callExpression(
t.memberExpression(reactRelayIdent, t.identifier(containerName)),
((otherArgs: any): Array<any>)
);
},
createOperationDescriptor("REACT_RELAY_MOCK_CONTAINER", { propName: containerName }),
{ skipInvariant: true, isPure: true }
);
invariant(value instanceof AbstractValue);

View File

@ -25,13 +25,13 @@ import {
} from "../../values/index.js";
import { To, Path } from "../../singletons.js";
import { ValuesDomain } from "../../domains/index.js";
import * as t from "@babel/types";
import type { BabelNodeExpression, BabelNodeSpreadElement } from "@babel/types";
import invariant from "../../invariant.js";
import { createAbstract, parseTypeNameOrTemplate } from "./utils.js";
import { describeValue } from "../../utils.js";
import { valueIsKnownReactAbstraction } from "../../react/utils.js";
import { CompilerDiagnostic, FatalError } from "../../errors.js";
import * as t from "@babel/types";
import { createOperationDescriptor, type OperationDescriptor } from "../../utils/generator.js";
export function createAbstractFunction(realm: Realm, ...additionalValues: Array<ConcreteValue>): NativeFunctionValue {
return new NativeFunctionValue(
@ -301,8 +301,11 @@ export default function(realm: Realm): void {
invariant(f instanceof FunctionValue);
f.isResidual = true;
if (unsafe) f.isUnsafeResidual = true;
let result = AbstractValue.createTemporalFromBuildFunction(realm, type, [f].concat(args), nodes =>
t.callExpression(nodes[0], ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>))
let result = AbstractValue.createTemporalFromBuildFunction(
realm,
type,
[f].concat(args),
createOperationDescriptor("RESIDUAL_CALL")
);
if (template) {
invariant(
@ -312,7 +315,7 @@ export default function(realm: Realm): void {
template.makePartial();
result.values = new ValuesDomain(new Set([template]));
invariant(realm.generator);
realm.rebuildNestedProperties(result, result.getIdentifier().name);
realm.rebuildNestedProperties(result, result.getIdentifier());
}
return result;
}
@ -322,13 +325,13 @@ export default function(realm: Realm): void {
function createNativeFunctionForResidualInjection(
name: string,
initializeAndValidateArgs: (Array<Value>) => void,
buildNode_: (Array<BabelNodeExpression>) => BabelNodeStatement,
operationDescriptor: OperationDescriptor,
numArgs: number
): NativeFunctionValue {
return new NativeFunctionValue(realm, "global." + name, name, numArgs, (context, ciArgs) => {
initializeAndValidateArgs(ciArgs);
invariant(realm.generator !== undefined);
realm.generator.emitStatement(ciArgs, buildNode_);
realm.generator.emitStatement(ciArgs, operationDescriptor);
return realm.intrinsics.undefined;
});
}
@ -351,13 +354,7 @@ export default function(realm: Realm): void {
}
Path.pushAndRefine(c);
},
([c, s]: Array<BabelNodeExpression>) => {
let errorLiteral = s.type === "StringLiteral" ? s : t.stringLiteral("Assumption violated");
return t.ifStatement(
t.unaryExpression("!", c),
t.blockStatement([t.throwStatement(t.newExpression(t.identifier("Error"), [errorLiteral]))])
);
},
createOperationDescriptor("ASSUME_CALL"),
2
),
writable: true,

View File

@ -23,9 +23,9 @@ import buildExpressionTemplate from "../../utils/builder.js";
import { ValuesDomain } from "../../domains/index.js";
import { describeLocation } from "../ecma262/Error.js";
import { To } from "../../singletons.js";
import AbstractObjectValue from "../../values/AbstractObjectValue";
import AbstractObjectValue from "../../values/AbstractObjectValue.js";
import { CompilerDiagnostic, FatalError } from "../../errors.js";
import { Utils } from "../../singletons";
import { Utils } from "../../singletons.js";
import invariant from "../../invariant.js";
const throwTemplateSrc = "(function(){throw new global.Error('abstract value defined at ' + A);})()";

View File

@ -12,9 +12,9 @@
import type { Realm } from "../realm.js";
import type { DataBlock, ElementType } from "../types.js";
import { Value, ObjectValue, NumberValue, EmptyValue, NullValue, UndefinedValue } from "../values/index.js";
import { SpeciesConstructor } from "../methods/construct.js";
import { IsConstructor } from "../methods/index.js";
import { IsDetachedBuffer } from "../methods/is.js";
import { SpeciesConstructor } from "./construct.js";
import { IsConstructor } from "./index.js";
import { IsDetachedBuffer } from "./is.js";
import { Create, Properties, To } from "../singletons.js";
import invariant from "../invariant.js";
import { ElementSize } from "../types.js";

View File

@ -32,13 +32,13 @@ import {
AbstractValue,
} from "../values/index.js";
import { GetIterator, HasSomeCompatibleType, IsCallable, IsPropertyKey, IteratorStep, IteratorValue } from "./index.js";
import { GeneratorStart } from "../methods/generator.js";
import { GeneratorStart } from "./generator.js";
import { ReturnCompletion, AbruptCompletion, ThrowCompletion, ForkedAbruptCompletion } from "../completions.js";
import { GetTemplateObject, GetV, GetThisValue } from "../methods/get.js";
import { GetTemplateObject, GetV, GetThisValue } from "./get.js";
import { Create, Environment, Functions, Join, Havoc, To, Widen } from "../singletons.js";
import invariant from "../invariant.js";
import { createOperationDescriptor } from "../utils/generator.js";
import type { BabelNodeExpression, BabelNodeSpreadElement, BabelNodeTemplateLiteral } from "@babel/types";
import * as t from "@babel/types";
// ECMA262 12.3.6.1
export function ArgumentListEvaluation(
@ -528,10 +528,7 @@ export function EvaluateDirectCallWithArgList(
realm,
func.functionResultType || Value,
[func].concat(argList),
(nodes: Array<BabelNodeExpression>) => {
let fun_args = nodes.slice(1);
return t.callExpression(nodes[0], ((fun_args: any): Array<BabelNodeExpression | BabelNodeSpreadElement>));
}
createOperationDescriptor("DIRECT_CALL_WITH_ARG_LIST")
);
}
func = func.throwIfNotConcrete();
@ -594,16 +591,20 @@ export function Call(realm: Realm, F: Value, V: Value, argsList?: Array<Value>):
}
if (V === realm.intrinsics.undefined) {
let fullArgs = [F].concat(argsList);
return AbstractValue.createTemporalFromBuildFunction(realm, Value, fullArgs, nodes => {
let fun_args = ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(nodes[0], fun_args);
});
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
fullArgs,
createOperationDescriptor("CALL_ABSTRACT_FUNC")
);
} else {
let fullArgs = [F, V].concat(argsList);
return AbstractValue.createTemporalFromBuildFunction(realm, Value, fullArgs, nodes => {
let fun_args = ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(t.memberExpression(nodes[0], t.identifier("call")), fun_args);
});
return AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
fullArgs,
createOperationDescriptor("CALL_ABSTRACT_FUNC_THIS")
);
}
}
invariant(F instanceof ObjectValue);

View File

@ -10,7 +10,6 @@
/* @flow */
import type { Realm } from "../realm.js";
import * as t from "@babel/types";
import invariant from "../invariant.js";
import type { PropertyKeyValue } from "../types.js";
import {
@ -64,6 +63,7 @@ import type {
BabelNodeLVal,
BabelNodePattern,
} from "@babel/types";
import * as t from "@babel/types";
export class EnvironmentImplementation {
// 2.6 RestBindingInitialization (please suggest an appropriate section name)

View File

@ -41,7 +41,6 @@ import traverseFast from "../utils/traverse-fast.js";
import invariant from "../invariant.js";
import parse from "../utils/parse.js";
import IsStrict from "../utils/strict.js";
import * as t from "@babel/types";
import type {
BabelNode,
BabelNodeBlockStatement,
@ -62,6 +61,7 @@ import type {
BabelNodeWhileStatement,
BabelNodeWithStatement,
} from "@babel/types";
import * as t from "@babel/types";
function InternalCall(
realm: Realm,

View File

@ -42,8 +42,7 @@ import {
import { Create, Environment, Join, Path, To } from "../singletons.js";
import invariant from "../invariant.js";
import type { BabelNodeTemplateLiteral } from "@babel/types";
import * as t from "@babel/types";
import { memberExpressionHelper } from "../utils/babelhelpers.js";
import { createOperationDescriptor } from "../utils/generator.js";
// ECMA262 7.3.22
export function GetFunctionRealm(realm: Realm, obj: ObjectValue): Realm {
@ -565,7 +564,7 @@ export function GetFromArrayWithWidenedNumericProperty(realm: Realm, arr: ArrayV
realm,
NumberValue,
[arr],
([o]) => t.memberExpression(o, t.identifier("length"), false),
createOperationDescriptor("UNKNOWN_ARRAY_LENGTH"),
{ skipInvariant: true, isPure: true }
);
}
@ -583,7 +582,7 @@ export function GetFromArrayWithWidenedNumericProperty(realm: Realm, arr: ArrayV
realm,
Value,
[arr, prop],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("UNKNOWN_ARRAY_GET_PARTIAL"),
{
skipInvariant: true,
isPure: true,

View File

@ -12,10 +12,10 @@
import type { Realm } from "../realm.js";
import { Value, ObjectValue, NumberValue, UndefinedValue, StringValue } from "../values/index.js";
import { Create, To } from "../singletons.js";
import { Get } from "../methods/get.js";
import { Call } from "../methods/call.js";
import { IsArray } from "../methods/is.js";
import { EnumerableOwnProperties } from "../methods/own.js";
import { Get } from "./get.js";
import { Call } from "./call.js";
import { IsArray } from "./is.js";
import { EnumerableOwnProperties } from "./own.js";
import type { PropertyKeyValue } from "../types.js";
import invariant from "../invariant.js";

View File

@ -20,12 +20,12 @@ import {
FunctionValue,
type UndefinedValue,
} from "../values/index.js";
import { SameValue } from "../methods/abstract.js";
import { Construct } from "../methods/construct.js";
import { Get } from "../methods/get.js";
import { Invoke, Call } from "../methods/call.js";
import { IsCallable, IsConstructor, IsPromise } from "../methods/is.js";
import { IteratorStep, IteratorValue } from "../methods/iterator.js";
import { SameValue } from "./abstract.js";
import { Construct } from "./construct.js";
import { Get } from "./get.js";
import { Invoke, Call } from "./call.js";
import { IsCallable, IsConstructor, IsPromise } from "./is.js";
import { IteratorStep, IteratorValue } from "./iterator.js";
import { Create, Properties } from "../singletons.js";
import invariant from "../invariant.js";

View File

@ -26,7 +26,7 @@ import {
UndefinedValue,
Value,
} from "../values/index.js";
import { EvalPropertyName } from "../evaluators/ObjectExpression";
import { EvalPropertyName } from "../evaluators/ObjectExpression.js";
import { EnvironmentRecord, Reference } from "../environment.js";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import invariant from "../invariant.js";
@ -45,12 +45,12 @@ import {
MakeConstructor,
SameValue,
SameValuePartial,
} from "../methods/index.js";
} from "./index.js";
import { type BabelNodeObjectMethod, type BabelNodeClassMethod, isValidIdentifier } from "@babel/types";
import type { LexicalEnvironment } from "../environment.js";
import { Create, Environment, Functions, Havoc, Join, Path, To } from "../singletons.js";
import IsStrict from "../utils/strict.js";
import { memberExpressionHelper } from "../utils/babelhelpers.js";
import { createOperationDescriptor } from "../utils/generator.js";
function StringKey(key: PropertyKeyValue): string {
if (key instanceof StringValue) key = key.value;
@ -1180,11 +1180,16 @@ export class PropertiesImplementation {
}
invariant(realm.generator);
let propName = P;
if (P instanceof StringValue) {
propName = P.value;
}
invariant(typeof propName === "string");
let absVal = AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[O._templateFor || O],
([node]) => memberExpressionHelper(node, StringKey(P)),
createOperationDescriptor("ABSTRACT_PROPERTY", { propName }),
{ isPure: true }
);
// TODO: We can't be sure what the descriptor will be, but the value will be abstract.
@ -1206,16 +1211,13 @@ export class PropertiesImplementation {
invariant(realm.generator);
let absVal;
function createAbstractPropertyValue(type: typeof Value) {
invariant(typeof P === "string");
if (O.isTransitivelySimple()) {
invariant(typeof P === "string");
return AbstractValue.createFromBuildFunction(
realm,
type,
[O._templateFor || O],
([node]) => {
invariant(typeof P === "string");
return memberExpressionHelper(node, P);
},
createOperationDescriptor("ABSTRACT_PROPERTY", { propName: P }),
{ kind: AbstractValue.makeKind("property", P) }
);
} else {
@ -1223,10 +1225,7 @@ export class PropertiesImplementation {
realm,
type,
[O._templateFor || O],
([node]) => {
invariant(typeof P === "string");
return memberExpressionHelper(node, P);
},
createOperationDescriptor("ABSTRACT_PROPERTY", { propName: P }),
{ skipInvariant: true, isPure: true }
);
}
@ -1320,7 +1319,8 @@ export class PropertiesImplementation {
if (value.kind !== "resolved") {
let realmGenerator = realm.generator;
invariant(realmGenerator);
value = realmGenerator.deriveAbstract(value.types, value.values, value.args, value.getBuildNode(), {
invariant(value.operationDescriptor);
value = realmGenerator.deriveAbstract(value.types, value.values, value.args, value.operationDescriptor, {
isPure: true,
kind: "resolved",
// We can't emit the invariant here otherwise it'll assume the AbstractValue's type not the union type

View File

@ -35,7 +35,7 @@ import {
Value,
} from "../values/index.js";
import invariant from "../invariant.js";
import * as t from "@babel/types";
import { createOperationDescriptor } from "../utils/generator.js";
type ElementConvType = {
Int8: (Realm, numberOrValue) => number,
@ -746,9 +746,9 @@ export class ToImplementation {
ToStringAbstract(realm: Realm, value: AbstractValue): AbstractValue {
if (value.mightNotBeString()) {
// If the property is not a string we need to coerce it.
let coerceToString = ([p]) => t.binaryExpression("+", t.stringLiteral(""), p);
let result;
// If the property is not a string we need to coerce it.
let coerceToString = createOperationDescriptor("COERCE_TO_STRING");
if (value.mightNotBeNumber() && !value.isSimpleObject()) {
// If this might be a non-simple object, we need to coerce this at a
// temporal point since it can have side-effects.

View File

@ -21,11 +21,11 @@ import {
NumberValue,
UndefinedValue,
} from "../values/index.js";
import { GetPrototypeFromConstructor } from "../methods/get.js";
import { AllocateArrayBuffer } from "../methods/arraybuffer.js";
import { IsDetachedBuffer, IsInteger } from "../methods/is.js";
import { GetValueFromBuffer, SetValueInBuffer } from "../methods/arraybuffer.js";
import { Construct, SpeciesConstructor } from "../methods/construct.js";
import { GetPrototypeFromConstructor } from "./get.js";
import { AllocateArrayBuffer } from "./arraybuffer.js";
import { IsDetachedBuffer, IsInteger } from "./is.js";
import { GetValueFromBuffer, SetValueInBuffer } from "./arraybuffer.js";
import { Construct, SpeciesConstructor } from "./construct.js";
import { To } from "../singletons.js";
import invariant from "../invariant.js";

View File

@ -17,13 +17,11 @@ import type { Descriptor, PropertyBinding } from "../types.js";
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
import { Reference } from "../environment.js";
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "../methods/index.js";
import { Generator } from "../utils/generator.js";
import { cloneDescriptor, equalDescriptors, IsDataDescriptor, StrictEqualityComparison } from "./index.js";
import { Generator, createOperationDescriptor } from "../utils/generator.js";
import { AbstractValue, ArrayValue, EmptyValue, Value } from "../values/index.js";
import invariant from "../invariant.js";
import * as t from "@babel/types";
import { memberExpressionHelper } from "../utils/babelhelpers.js";
export class WidenImplementation {
_widenArrays(
@ -158,7 +156,7 @@ export class WidenImplementation {
result.types,
result.values,
[b.value || realm.intrinsics.undefined],
([n]) => n,
createOperationDescriptor("SINGLE_ARG"),
{ skipInvariant: true }
);
b.phiNode = phiNode;
@ -167,7 +165,7 @@ export class WidenImplementation {
invariant(phiNode.intrinsicName !== undefined);
let phiName = phiNode.intrinsicName;
result.intrinsicName = phiName;
result._buildNode = args => t.identifier(phiName);
result.operationDescriptor = createOperationDescriptor("WIDENED_IDENTIFIER", { id: phiName });
}
invariant(result instanceof Value);
let previousHasLeaked = b2.previousHasLeaked;
@ -253,7 +251,7 @@ export class WidenImplementation {
let pathNode = b.pathNode;
if (pathNode === undefined) {
//Since properties already have mutable storage locations associated with them, we do not
//need phi nodes. What we need is an abstract value with a build node that results in a memberExpression
//need phi nodes. What we need is an abstract value with a operation descriptor that results in a memberExpression
//that resolves to the storage location of the property.
// For now, we only handle loop invariant properties
@ -264,14 +262,20 @@ export class WidenImplementation {
(key instanceof AbstractValue && !(key.mightNotBeString() && key.mightNotBeNumber()))
) {
if (typeof key === "string") {
pathNode = AbstractValue.createFromWidenedProperty(realm, rval, [b.object], ([o]) =>
memberExpressionHelper(o, key)
pathNode = AbstractValue.createFromWidenedProperty(
realm,
rval,
[b.object],
createOperationDescriptor("WIDEN_PROPERTY", { propName: key })
);
} else {
invariant(key instanceof AbstractValue);
pathNode = AbstractValue.createFromWidenedProperty(realm, rval, [b.object, key], ([o, p]) => {
return memberExpressionHelper(o, p);
});
pathNode = AbstractValue.createFromWidenedProperty(
realm,
rval,
[b.object, key],
createOperationDescriptor("WIDEN_ABSTRACT_PROPERTY")
);
}
// The value of the property at the start of the loop needs to be written to the property
// before the loop commences, otherwise the memberExpression will result in an undefined value.
@ -283,14 +287,19 @@ export class WidenImplementation {
if (key === "length" && b.object instanceof ArrayValue) {
// do nothing, the array length will already be initialized
} else if (typeof key === "string") {
generator.emitVoidExpression(rval.types, rval.values, [b.object, initVal], ([o, v]) => {
invariant(typeof key === "string");
return t.assignmentExpression("=", memberExpressionHelper(o, key), v);
});
generator.emitVoidExpression(
rval.types,
rval.values,
[b.object, initVal],
createOperationDescriptor("WIDEN_PROPERTY_ASSIGNMENT", { propName: key })
);
} else {
invariant(key instanceof AbstractValue);
generator.emitVoidExpression(rval.types, rval.values, [b.object, key, initVal], ([o, p, v]) =>
t.assignmentExpression("=", memberExpressionHelper(o, p), v)
generator.emitVoidExpression(
rval.types,
rval.values,
[b.object, key, initVal],
createOperationDescriptor("WIDEN_ABSTRACT_PROPERTY_ASSIGNMENT")
);
}
}

View File

@ -22,7 +22,7 @@ import {
Value,
} from "../values/index.js";
import invariant from "../invariant.js";
import { hardModifyReactObjectPropertyBinding, isReactElement, isReactPropsObject, getProperty } from "./utils";
import { hardModifyReactObjectPropertyBinding, isReactElement, isReactPropsObject, getProperty } from "./utils.js";
import { ResidualReactElementVisitor } from "../serializer/ResidualReactElementVisitor.js";
export type ReactSetValueMapKey = Value | number | string;

View File

@ -31,8 +31,9 @@ import {
forEachArrayValue,
getProperty,
mapArrayValue,
} from "./utils";
} from "./utils.js";
import { ExpectedBailOut } from "./errors.js";
import { createOperationDescriptor } from "../utils/generator.js";
// Branch status is used for when Prepack returns an abstract value from a render
// that results in a conditional path occuring. This can be problematic for reconcilation
@ -243,7 +244,7 @@ export function wrapReactElementInBranchOrReturnValue(realm: Realm, value: Value
realm,
ObjectValue,
[cloneReactElement(realm, value, false)],
([node]) => node,
createOperationDescriptor("SINGLE_ARG"),
{ isPure: true, skipInvariant: true }
);
invariant(temporal instanceof AbstractObjectValue);

View File

@ -28,7 +28,7 @@ import {
getValueFromFunctionCall,
hardModifyReactObjectPropertyBinding,
valueIsClassComponent,
} from "./utils";
} from "./utils.js";
import { ExpectedBailOut, SimpleClassBailOut } from "./errors.js";
import { Get, Construct } from "../methods/index.js";
import { Properties } from "../singletons.js";

View File

@ -31,9 +31,9 @@ import {
getProperty,
hasNoPartialKeyOrRef,
} from "./utils.js";
import * as t from "@babel/types";
import { computeBinary } from "../evaluators/BinaryExpression.js";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import { createOperationDescriptor } from "../utils/generator.js";
function createPropsObject(
realm: Realm,
@ -194,9 +194,7 @@ function createPropsObject(
realm,
ObjectValue,
temporalArgs,
([methodNode, ..._args]) => {
return t.callExpression(methodNode, ((_args: any): Array<any>));
},
createOperationDescriptor("REACT_DEFAULT_PROPS_HELPER"),
{ skipInvariant: true }
);
invariant(temporalTo instanceof AbstractObjectValue);

View File

@ -68,13 +68,17 @@ import {
// $FlowFixMe: flow complains that this isn't a module but it is, and it seems to load fine
import hyphenateStyleName from "fbjs/lib/hyphenateStyleName";
import { To } from "../../singletons.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export type ReactNode = Array<ReactNode> | string | AbstractValue | ArrayValue;
function renderValueWithHelper(realm: Realm, value: Value, helper: ECMAScriptSourceFunctionValue): AbstractValue {
// given we know nothing of this value, we need to escape the contents of it at runtime
let val = AbstractValue.createFromBuildFunction(realm, Value, [helper, value], ([helperNode, valueNode]) =>
t.callExpression(helperNode, [valueNode])
let val = AbstractValue.createFromBuildFunction(
realm,
Value,
[helper, value],
createOperationDescriptor("REACT_SSR_RENDER_VALUE_HELPER")
);
invariant(val instanceof AbstractValue);
return val;
@ -252,8 +256,11 @@ function renderReactNode(realm: Realm, reactNode: ReactNode): StringValue | Abst
args.push(element);
}
}
let val = AbstractValue.createFromBuildFunction(realm, StringValue, args, valueNodes =>
t.templateLiteral(((quasis: any): Array<any>), valueNodes)
let val = AbstractValue.createFromBuildFunction(
realm,
StringValue,
args,
createOperationDescriptor("REACT_SSR_TEMPLATE_LITERAL", { quasis })
);
invariant(val instanceof AbstractValue);
return val;
@ -490,13 +497,9 @@ export function renderToString(
invariant(realm.generator);
// create a single regex used for the escape functions
// by hoisting it, it gets cached by the VM JITs
realm.generator.emitStatement([], () =>
t.variableDeclaration("var", [t.variableDeclarator(t.identifier("matchHtmlRegExp"), t.regExpLiteral("[\"'&<>]"))])
);
realm.generator.emitStatement([], createOperationDescriptor("REACT_SSR_REGEX_CONSTANT"));
invariant(realm.generator);
realm.generator.emitStatement([], () =>
t.variableDeclaration("var", [t.variableDeclarator(t.identifier("previousWasTextNode"), t.booleanLiteral(false))])
);
realm.generator.emitStatement([], createOperationDescriptor("REACT_SSR_PREV_TEXT_NODE"));
invariant(effects);
realm.applyEffects(effects);
invariant(effects.result instanceof SimpleNormalCompletion);

View File

@ -19,7 +19,7 @@ import type {
BabelNodeStringLiteral,
} from "@babel/types";
import invariant from "../invariant.js";
import { isReactComponent } from "./utils";
import { isReactComponent } from "./utils.js";
export function convertExpressionToJSXIdentifier(
expr: BabelNodeExpression,

View File

@ -47,7 +47,7 @@ import {
valueIsFactoryClassComponent,
valueIsKnownReactAbstraction,
valueIsLegacyCreateClassComponent,
} from "./utils";
} from "./utils.js";
import { Get } from "../methods/index.js";
import invariant from "../invariant.js";
import { Properties } from "../singletons.js";
@ -57,7 +57,6 @@ import {
getValueWithBranchingLogicApplied,
wrapReactElementInBranchOrReturnValue,
} from "./branching.js";
import * as t from "@babel/types";
import { Completion } from "../completions.js";
import {
getInitialProps,
@ -79,6 +78,8 @@ import {
import { Logger } from "../utils/logger.js";
import type { ClassComponentMetadata, ReactComponentTreeConfig, ReactHint } from "../types.js";
import { handleReportedSideEffect } from "../serializer/utils.js";
import { createOperationDescriptor } from "../utils/generator.js";
import * as t from "@babel/types";
type ComponentResolutionStrategy =
| "NORMAL"
@ -900,9 +901,7 @@ export class Reconciler {
this.realm,
ObjectValue,
[reactDomPortalFunc, resolvedReactPortalValue, domNodeValue],
([renderNode, ..._args]) => {
return t.callExpression(renderNode, ((_args: any): Array<any>));
},
createOperationDescriptor("REACT_TEMPORAL_FUNC"),
{ skipInvariant: true, isPure: true }
);
}

View File

@ -64,12 +64,15 @@ import {
import type { Compatibility, RealmOptions, ReactOutputTypes, InvariantModeTypes } from "./options.js";
import invariant from "./invariant.js";
import seedrandom from "seedrandom";
import { Generator, PreludeGenerator, type TemporalBuildNodeEntry } from "./utils/generator.js";
import { emptyExpression, voidExpression } from "./utils/babelhelpers.js";
import {
createOperationDescriptor,
Generator,
PreludeGenerator,
type TemporalBuildNodeEntry,
} from "./utils/generator.js";
import { Environment, Functions, Join, Properties, To, Widen, Path } from "./singletons.js";
import type { ReactSymbolTypes } from "./react/utils.js";
import type { BabelNode, BabelNodeSourceLocation, BabelNodeLVal, BabelNodeStatement } from "@babel/types";
import * as t from "@babel/types";
import { Utils } from "./singletons.js";
export type BindingEntry = {
@ -1018,12 +1021,16 @@ export class Realm {
// Generate code using effects2 because its expressions have not been widened away.
const e2 = effects2;
this._applyPropertiesToNewlyCreatedObjects(e2.modifiedProperties, e2.createdObjects);
this._emitPropertAssignments(e2.generator, e2.modifiedProperties, e2.createdObjects);
this._emitPropertyAssignments(e2.generator, e2.modifiedProperties, e2.createdObjects);
this._emitLocalAssignments(e2.generator, e2.modifiedBindings, e2.createdObjects);
invariant(test instanceof AbstractValue);
let cond = e2.generator.deriveAbstract(test.types, test.values, [test], ([n]) => n, {
skipInvariant: true,
});
let cond = e2.generator.deriveAbstract(
test.types,
test.values,
[test],
createOperationDescriptor("SINGLE_ARG"),
{ skipInvariant: true }
);
return [effects1, effects2, cond];
}
effects1 = Widen.widenEffects(this, effects1, effects2);
@ -1109,10 +1116,8 @@ export class Realm {
bindings.forEach((binding, key, map) => {
let val = binding.value;
if (val instanceof AbstractValue) {
invariant(val._buildNode !== undefined);
let tval = gen.deriveAbstract(val.types, val.values, [val], ([n]) => n, {
skipInvariant: true,
});
invariant(val.operationDescriptor !== undefined);
let tval = gen.deriveAbstract(val.types, val.values, [val], createOperationDescriptor("SINGLE_ARG"));
tvalFor.set(key, tval);
}
});
@ -1122,36 +1127,18 @@ export class Realm {
let phiNode = key.phiNode;
let tval = tvalFor.get(key);
invariant(tval !== undefined);
gen.emitStatement([tval], ([v]) => {
invariant(phiNode !== undefined);
let id = phiNode.buildNode([]);
return t.expressionStatement(t.assignmentExpression("=", (id: any), v));
});
gen.emitStatement([tval], createOperationDescriptor("LOCAL_ASSIGNMENT", { value: phiNode }));
}
if (val instanceof ObjectValue && newlyCreatedObjects.has(val)) {
let phiNode = key.phiNode;
gen.emitStatement([val], ([v]) => {
invariant(phiNode !== undefined);
let id = phiNode.buildNode([]);
return t.expressionStatement(t.assignmentExpression("=", (id: any), v));
});
gen.emitStatement([val], createOperationDescriptor("LOCAL_ASSIGNMENT", { value: phiNode }));
}
});
}
// populate the loop body generator with assignments that will update properties modified inside the loop
_emitPropertAssignments(gen: Generator, pbindings: PropertyBindings, newlyCreatedObjects: CreatedObjects): void {
function isSelfReferential(value: Value, pathNode: void | AbstractValue): boolean {
if (value === pathNode) return true;
if (value instanceof AbstractValue && pathNode !== undefined) {
for (let v of value.args) {
if (isSelfReferential(v, pathNode)) return true;
}
}
return false;
}
_emitPropertyAssignments(gen: Generator, pbindings: PropertyBindings, newlyCreatedObjects: CreatedObjects): void {
let tvalFor: Map<any, AbstractValue> = new Map();
pbindings.forEach((val, key, map) => {
if (newlyCreatedObjects.has(key.object) || key.object.refuseSerialization) {
@ -1159,20 +1146,12 @@ export class Realm {
}
let value = val && val.value;
if (value instanceof AbstractValue) {
invariant(value._buildNode !== undefined);
invariant(value.operationDescriptor !== undefined);
let tval = gen.deriveAbstract(
value.types,
value.values,
[key.object, value],
([o, n]) => {
invariant(value instanceof Value);
if (typeof key.key === "string" && value.mightHaveBeenDeleted() && isSelfReferential(value, key.pathNode)) {
let inTest = t.binaryExpression("in", t.stringLiteral(key.key), o);
let addEmpty = t.conditionalExpression(inTest, n, emptyExpression);
n = t.logicalExpression("||", n, addEmpty);
}
return n;
},
createOperationDescriptor("LOGICAL_PROPERTY_ASSIGNMENT", { binding: key, value }),
{
skipInvariant: true,
}
@ -1189,27 +1168,13 @@ export class Realm {
invariant(val !== undefined);
let value = val.value;
invariant(value instanceof Value);
let mightHaveBeenDeleted = value.mightHaveBeenDeleted();
let mightBeUndefined = value.mightBeUndefined();
let keyKey = key.key;
if (typeof keyKey === "string") {
if (path !== undefined) {
gen.emitStatement([key.object, tval || value, this.intrinsics.empty], ([o, v, e]) => {
invariant(path !== undefined);
invariant(typeof keyKey === "string");
let lh = path.buildNode([o, t.identifier(keyKey)]);
let r = t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
if (mightHaveBeenDeleted) {
// If v === __empty || (v === undefined && !(key.key in o)) then delete it
let emptyTest = t.binaryExpression("===", v, e);
let undefinedTest = t.binaryExpression("===", v, voidExpression);
let inTest = t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(keyKey), o));
let guard = t.logicalExpression("||", emptyTest, t.logicalExpression("&&", undefinedTest, inTest));
let deleteIt = t.expressionStatement(t.unaryExpression("delete", (lh: any)));
return t.ifStatement(mightBeUndefined ? emptyTest : guard, deleteIt, r);
}
return r;
});
gen.emitStatement(
[key.object, tval || value, this.intrinsics.empty],
createOperationDescriptor("CONDITIONAL_PROPERTY_ASSIGNMENT", { binding: key, path, value })
);
} else {
// RH value was not widened, so it must have been a constant. We don't need to assign that inside the loop.
// Note, however, that if the LH side is a property of an intrinsic object, then an assignment will
@ -1218,11 +1183,10 @@ export class Realm {
} else {
// TODO: What if keyKey is undefined?
invariant(keyKey instanceof Value);
gen.emitStatement([key.object, keyKey, tval || value, this.intrinsics.empty], ([o, p, v, e]) => {
invariant(path !== undefined);
let lh = path.buildNode([o, p]);
return t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
});
gen.emitStatement(
[key.object, keyKey, tval || value, this.intrinsics.empty],
createOperationDescriptor("PROPERTY_ASSIGNMENT", { path })
);
}
});
}
@ -1699,11 +1663,10 @@ export class Realm {
propertyValue.intrinsicName = `${path}.${key}`;
propertyValue.kind = "rebuiltProperty";
propertyValue.args = [object];
propertyValue._buildNode = ([node]) =>
t.isValidIdentifier(key)
? t.memberExpression(node, t.identifier(key), false)
: t.memberExpression(node, t.stringLiteral(key), true);
this.rebuildNestedProperties(propertyValue, propertyValue.intrinsicName);
propertyValue.operationDescriptor = createOperationDescriptor("REBUILT_OBJECT", { propName: key });
let intrinsicName = propertyValue.intrinsicName;
invariant(intrinsicName !== undefined);
this.rebuildNestedProperties(propertyValue, intrinsicName);
}
}

View File

@ -88,7 +88,7 @@ export class Emitter {
onAbstractValueWithIdentifier: val => {
// If the value hasn't been declared yet, then we should wait for it.
if (
derivedIds.has(val.getIdentifier().name) &&
derivedIds.has(val.getIdentifier()) &&
!this.cannotDeclare() &&
!this.hasBeenDeclared(val) &&
(!this.emittingToAdditionalFunction() || referencedDeclaredValues.get(val) !== undefined)

View File

@ -37,7 +37,7 @@ import type {
} from "@babel/types";
import type { FunctionBodyAstNode } from "../types.js";
import type { FactoryFunctionInfo } from "./types.js";
import { nullExpression } from "../utils/babelhelpers.js";
import { nullExpression } from "../utils/babelhelpers";
function canShareFunctionBody(duplicateFunctionInfo: FactoryFunctionInfo): boolean {
if (duplicateFunctionInfo.anyContainingAdditionalFunction) {

View File

@ -42,7 +42,7 @@ import type {
BabelNodeFunctionExpression,
} from "@babel/types";
import { Generator, PreludeGenerator, NameGenerator } from "../utils/generator.js";
import type { SerializationContext } from "../utils/generator.js";
import type { OperationDescriptor, SerializationContext } from "../utils/generator.js";
import invariant from "../invariant.js";
import type {
ResidualFunctionBinding,
@ -81,9 +81,10 @@ import type { Binding } from "../environment.js";
import { GlobalEnvironmentRecord, DeclarativeEnvironmentRecord, FunctionEnvironmentRecord } from "../environment.js";
import type { Referentializer } from "./Referentializer.js";
import { GeneratorDAG } from "./GeneratorDAG.js";
import { type Replacement, getReplacement } from "./ResidualFunctionInstantiator";
import { type Replacement, getReplacement } from "./ResidualFunctionInstantiator.js";
import { describeValue } from "../utils.js";
import { getAsPropertyNameExpression } from "../utils/babelhelpers.js";
import { ResidualOperationSerializer } from "./ResidualOperationSerializer.js";
function commentStatement(text: string) {
let s = t.emptyStatement();
@ -142,6 +143,7 @@ export class ResidualHeapSerializer {
let realmPreludeGenerator = this.realm.preludeGenerator;
invariant(realmPreludeGenerator);
this.preludeGenerator = realmPreludeGenerator;
this.residualOperationSerializer = new ResidualOperationSerializer(realm, realmPreludeGenerator);
this.prelude = [];
this._descriptors = new Map();
@ -231,6 +233,7 @@ export class ResidualHeapSerializer {
body: Array<BabelNodeStatement>;
mainBody: SerializedBody;
realm: Realm;
residualOperationSerializer: ResidualOperationSerializer;
preludeGenerator: PreludeGenerator;
generator: Generator;
_descriptors: Map<string, BabelNodeIdentifier>;
@ -385,7 +388,9 @@ export class ResidualHeapSerializer {
? t.memberExpression(uid, protoExpression)
: t.callExpression(this.preludeGenerator.memoizeReference("Object.getPrototypeOf"), [uid]);
let condition = t.binaryExpression("!==", fetchedPrototype, serializedProto);
let consequent = this.generator.getErrorStatement(t.stringLiteral("unexpected prototype"));
let consequent = this.residualOperationSerializer.getErrorStatement(
t.stringLiteral("unexpected prototype")
);
this.emitter.emit(t.ifStatement(condition, consequent));
},
this.emitter.getBody()
@ -1911,7 +1916,8 @@ export class ResidualHeapSerializer {
let prop = this.serializeValue(val.args[1]);
return t.memberExpression(obj, prop, true);
}
let serializedValue = val.buildNode(serializedArgs);
invariant(val.operationDescriptor !== undefined);
let serializedValue = this.residualOperationSerializer.serialize(val.operationDescriptor, serializedArgs);
if (serializedValue.type === "Identifier") {
let id = ((serializedValue: any): BabelNodeIdentifier);
invariant(
@ -2123,13 +2129,30 @@ export class ResidualHeapSerializer {
// along the code of the nested generator; their definitions need to get hoisted
// or repeated so that they are accessible and defined from all using scopes
let context = {
serializeOperationDescriptor: (
operationDescriptor: OperationDescriptor,
nodes: Array<BabelNodeExpression>,
_context: SerializationContext,
valuesToProcess: Set<AbstractValue | ObjectValue>
) => {
let serializedValue = this.residualOperationSerializer.serialize(
operationDescriptor,
nodes,
_context,
valuesToProcess
);
return ((serializedValue: any): BabelNodeStatement);
},
serializeValue: this.serializeValue.bind(this),
serializeBinding: this.serializeBinding.bind(this),
serializeGenerator: (
generator: Generator,
valuesToProcess: Set<AbstractValue | ObjectValue>
): Array<BabelNodeStatement> =>
this._withGeneratorScope("Generator", generator, valuesToProcess, () => generator.serialize(context)),
this._withGeneratorScope("Generator", generator, valuesToProcess, () =>
generator.serialize(((context: any): SerializationContext))
),
initGenerator: (generator: Generator) => {
let activeGeneratorBody = this._getActiveBodyOfGenerator(generator);
invariant(activeGeneratorBody === this.emitter.getBody(), "generator to init must be current emitter body");

View File

@ -0,0 +1,998 @@
/**
* 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 { Realm } from "../realm.js";
import {
Generator,
PreludeGenerator,
type SerializationContext,
type OperationDescriptor,
type OperationDescriptorData,
} from "../utils/generator.js";
import {
emptyExpression,
memberExpressionHelper,
nullExpression,
protoExpression,
voidExpression,
} from "../utils/babelhelpers.js";
import invariant from "../invariant.js";
import { type Binding } from "../environment.js";
import * as t from "@babel/types";
import { AbstractValue, EmptyValue, ObjectValue, Value } from "../values/index.js";
import type { BabelNodeBlockStatement, BabelNodeExpression, BabelNodeSpreadElement } from "@babel/types";
import { Utils } from "../singletons.js";
import type { PropertyBinding } from "../types.js";
const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function serializeBody(
generator: Generator,
context: SerializationContext,
valuesToProcess: Set<AbstractValue | ObjectValue>
): BabelNodeBlockStatement {
let statements = context.serializeGenerator(generator, valuesToProcess);
if (statements.length === 1 && statements[0].type === "BlockStatement") return (statements[0]: any);
return t.blockStatement(statements);
}
function isSelfReferential(value: Value, pathNode: void | AbstractValue): boolean {
if (value === pathNode) return true;
if (value instanceof AbstractValue && pathNode !== undefined) {
for (let v of value.args) {
if (isSelfReferential(v, pathNode)) return true;
}
}
return false;
}
export class ResidualOperationSerializer {
constructor(realm: Realm, preludeGenerator: PreludeGenerator) {
this.realm = realm;
this.preludeGenerator = preludeGenerator;
}
realm: Realm;
preludeGenerator: PreludeGenerator;
getErrorStatement(message: BabelNodeExpression): BabelNodeStatement {
if (this.realm.invariantMode === "throw")
return t.throwStatement(t.newExpression(this.preludeGenerator.memoizeReference("Error"), [message]));
else {
let targetReference = this.realm.invariantMode;
let args = [message];
let i = targetReference.indexOf("+");
if (i !== -1) {
let s = targetReference.substr(i + 1);
let x = Number.parseInt(s, 10);
args.push(isNaN(x) ? t.stringLiteral(s) : t.numericLiteral(x));
targetReference = targetReference.substr(0, i);
}
return t.expressionStatement(t.callExpression(this.preludeGenerator.memoizeReference(targetReference), args));
}
}
serialize(
operationDescriptor: OperationDescriptor,
nodes: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
): BabelNodeStatement | BabelNodeExpression {
let { data, kind, type } = operationDescriptor;
let babelNode;
switch (type) {
case "IDENTIFIER":
babelNode = this._serializeIdentifier(data);
break;
case "REBUILT_OBJECT":
babelNode = this._serializeRebuiltObject(data, nodes);
break;
case "FOR_IN":
babelNode = this._serializeForIn(data, nodes);
break;
case "DO_WHILE":
babelNode = this._serializeDoWhile(data, nodes, context, valuesToProcess);
break;
case "APPEND_GENERATOR":
babelNode = this._serializeAppendGenerator(data, nodes, context, valuesToProcess);
break;
case "JOIN_GENERATORS":
babelNode = this._serializeJoinGenerators(data, nodes, context, valuesToProcess);
break;
case "BINARY_EXPRESSION":
babelNode = this._serializeBinaryExpression(data, nodes);
break;
case "LOGICAL_EXPRESSION":
babelNode = this._serializeLogicalExpression(data, nodes);
break;
case "CONDITIONAL_EXPRESSION":
babelNode = this._serializeConditionalExpression(data, nodes);
break;
case "UNARY_EXPRESSION":
babelNode = this._serializeUnaryExpression(data, nodes);
break;
case "ABSTRACT_PROPERTY":
babelNode = this._serializeAbstractProperty(data, nodes);
break;
case "ABSTRACT_FROM_TEMPLATE":
babelNode = this._serializeAbstractFromTemplate(data, nodes);
break;
case "GLOBAL_ASSIGNMENT":
babelNode = this._serializeGlobalAssignment(data, nodes);
break;
case "GLOBAL_DELETE":
babelNode = this._serializeGlobalDelete(data);
break;
case "EMIT_PROPERTY_ASSIGNMENT":
babelNode = this._serializeEmitPropertyAssignment(data, nodes, context);
break;
case "PROPERTY_DELETE":
babelNode = this._serializePropertyDelete(data, nodes);
break;
case "THROW":
babelNode = this._serializeThrow(data, nodes);
break;
case "CONDITIONAL_THROW":
babelNode = this._serializeConditionalThrow(data, nodes, context);
break;
case "COERCE_TO_STRING":
babelNode = this._serializeCoerceToString(data, nodes);
break;
case "OBJECT_ASSIGN":
babelNode = this._serializeObjectAssign(data, nodes);
break;
case "SINGLE_ARG":
babelNode = this._serializeSingleArg(data, nodes);
break;
case "CALL_BAILOUT":
babelNode = this._serializeCallBailout(data, nodes);
break;
case "EMIT_CALL":
babelNode = this._serializeEmitCall(data, nodes);
break;
case "EMIT_CALL_AND_CAPTURE_RESULT":
babelNode = this._serializeEmitCallAndCaptureResults(data, nodes);
break;
case "CONCRETE_MODEL":
babelNode = this._serializeConcreteModel(data, nodes);
break;
case "NEW_EXPRESSION":
babelNode = this._serializeNewExpression(data, nodes);
break;
case "FOR_STATEMENT_FUNC":
babelNode = this._serializeForFunctionCall(data, nodes);
break;
case "GET_BINDING":
babelNode = this._serializeGetBinding(data, nodes, context);
break;
case "UNKNOWN_ARRAY_METHOD_CALL":
babelNode = this._serializeUnknownArrayMethodCall(data, nodes);
break;
case "UNKNOWN_ARRAY_METHOD_PROPERTY_CALL":
babelNode = this._serializeUnknownArrayMethodPropertyCall(data, nodes);
break;
case "UNKNOWN_ARRAY_LENGTH":
babelNode = this._serializeUnknownArrayLength(data, nodes);
break;
case "UNKNOWN_ARRAY_GET_PARTIAL":
babelNode = this._serializeUnknownArrayGetPartial(data, nodes);
break;
case "OBJECT_SET_PARTIAL":
babelNode = this._serializeObjectSetPartial(data, nodes);
break;
case "OBJECT_GET_PARTIAL":
babelNode = this._serializeObjectGetPartial(data, nodes);
break;
case "ABSTRACT_OBJECT_GET_PARTIAL":
babelNode = this._serializeAbstractObjectGetPartial(data, nodes);
break;
case "ABSTRACT_OBJECT_SET_PARTIAL":
babelNode = this._serializeAbstractObjectSetPartial(data, nodes);
break;
case "ABSTRACT_OBJECT_SET_PARTIAL_VALUE":
babelNode = this._serializeAbstractObjectSetPartialValue(data, nodes);
break;
case "ABSTRACT_OBJECT_GET_PROTO_OF":
babelNode = this._serializeAbstractObjectGetProtoOf(data, nodes);
break;
case "ABSTRACT_OBJECT_GET":
babelNode = this._serializeAbstractObjectGet(data, nodes);
break;
case "DEFINE_PROPERTY":
babelNode = this._serializeDefineProperty(data, nodes, context);
break;
case "OBJECT_PROTO_HAS_OWN_PROPERTY":
babelNode = this._serializeObjectProtoHasOwnProperty(data, nodes);
break;
case "OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR":
babelNode = this._serializeObjectProtoGetOwnPropertyDescriptor(data, nodes);
break;
case "DIRECT_CALL_WITH_ARG_LIST":
babelNode = this._serializeDirectCallWithArgList(data, nodes);
break;
case "CALL_ABSTRACT_FUNC":
babelNode = this._serializeCallAbstractFunc(data, nodes);
break;
case "CALL_ABSTRACT_FUNC_THIS":
babelNode = this._serializeCallAbstractFuncThis(data, nodes);
break;
case "LOCAL_ASSIGNMENT":
babelNode = this._serializeLocalAssignment(data, nodes, context, valuesToProcess);
break;
case "LOGICAL_PROPERTY_ASSIGNMENT":
babelNode = this._serializeLogicalPropertyAssignment(data, nodes);
break;
case "CONDITIONAL_PROPERTY_ASSIGNMENT":
babelNode = this._serializeConditionalPropertyAssignment(data, nodes, context, valuesToProcess);
break;
case "PROPERTY_ASSIGNMENT":
babelNode = this._serializePropertyAssignment(data, nodes, context, valuesToProcess);
break;
case "UPDATE_INCREMENTOR":
babelNode = this._serializeUpdateIncrementor(data, nodes);
break;
case "CONSOLE_LOG":
babelNode = this._serializeConsoleLog(data, nodes);
break;
case "MODULES_REQUIRE":
babelNode = this._serializeModulesRequires(data);
break;
case "RESIDUAL_CALL":
babelNode = this._serializeResidualCall(data, nodes);
break;
case "ASSUME_CALL":
babelNode = this._serializeAssumeCall(data, nodes);
break;
case "CANNOT_BECOME_OBJECT":
babelNode = this._serializeCannotBecomeObject(data, nodes);
break;
case "WIDENED_IDENTIFIER":
babelNode = this._serializeIdentifier(data);
break;
case "WIDEN_PROPERTY":
babelNode = this._serializeWidenProperty(data, nodes);
break;
case "WIDEN_ABSTRACT_PROPERTY":
babelNode = this._serializeWidenAbstractProperty(data, nodes);
break;
case "WIDEN_PROPERTY_ASSIGNMENT":
babelNode = this._serializeWidenPropertyAssignment(data, nodes);
break;
case "WIDEN_ABSTRACT_PROPERTY_ASSIGNMENT":
babelNode = this._serializeWidenAbstractPropertyAssignment(data, nodes);
break;
// Invariants
case "INVARIANT":
babelNode = this._serializeInvariant(data, nodes);
break;
case "DERIVED_ABSTRACT_INVARIANT":
babelNode = this._serializeDerivedAbstractInvariant(data, nodes);
break;
case "PROPERTY_INVARIANT":
babelNode = this._serializePropertyInvariant(data, nodes);
break;
case "INVARIANT_APPEND":
babelNode = this._serializeInvariantAppend(data, nodes);
break;
case "FULL_INVARIANT":
babelNode = this._serializeFullInvariant(data, nodes);
break;
case "FULL_INVARIANT_ABSTRACT":
babelNode = this._serializeFullInvariantAbstract(data, nodes);
break;
case "FULL_INVARIANT_FUNCTION":
babelNode = this._serializeFullInvariantFunction(data, nodes);
break;
// React
case "REACT_DEFAULT_PROPS_HELPER":
babelNode = this._serializeReactDefaultPropsHelper(data, nodes);
break;
case "REACT_SSR_REGEX_CONSTANT":
return t.variableDeclaration("var", [
t.variableDeclarator(t.identifier("matchHtmlRegExp"), t.regExpLiteral("[\"'&<>]")),
]);
case "REACT_SSR_PREV_TEXT_NODE":
return t.variableDeclaration("var", [
t.variableDeclarator(t.identifier("previousWasTextNode"), t.booleanLiteral(false)),
]);
case "REACT_SSR_RENDER_VALUE_HELPER":
babelNode = this._serializeReactRenderValueHelper(data, nodes);
break;
case "REACT_SSR_TEMPLATE_LITERAL":
babelNode = this._serializeReactSSRTemplateLiteral(data, nodes);
break;
case "REACT_TEMPORAL_FUNC":
babelNode = this._serializeReactTemporalFunc(data, nodes);
break;
case "REACT_CREATE_CONTEXT_PROVIDER":
babelNode = this._serializeReactCreateContextProvider(data, nodes);
break;
case "REACT_NATIVE_STRING_LITERAL":
babelNode = this._serializeReactNativeStringLiteral(data);
break;
case "REACT_RELAY_MOCK_CONTAINER":
babelNode = this._serializeReactRelayMockContainer(data, nodes);
break;
// FB Mocks
case "FB_MOCKS_BOOTLOADER_LOAD_MODULES":
babelNode = this._serializeFBMocksBootloaderLoadModules(data, nodes);
break;
case "FB_MOCKS_MAGIC_GLOBAL_FUNCTION":
babelNode = this._serializeFBMocksMagicGlobalFunction(data, nodes);
break;
// Babel helpers
case "BABEL_HELPERS_OBJECT_WITHOUT_PROPERTIES":
babelNode = this._serializeBabelHelpersObjectWithoutProperties(data, nodes);
break;
default:
invariant(false, `operation descriptor "type" not recognized when serializing operation descriptor`);
}
if (kind === "DERIVED") {
return this._serializeDerivedOperationDescriptor(data, ((babelNode: any): BabelNodeExpression));
} else if (kind === "VOID") {
return this._serializeVoidOperationDescriptor(((babelNode: any): BabelNodeExpression));
}
return babelNode;
}
_serializeAppendGenerator(
{ generator, propName: leadingComment }: OperationDescriptorData,
node: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
) {
invariant(context !== undefined);
invariant(generator !== undefined);
invariant(leadingComment !== undefined);
invariant(valuesToProcess !== undefined);
let statements = context.serializeGenerator(generator, valuesToProcess);
if (statements.length === 1) {
let statement = statements[0];
if (leadingComment.length > 0)
statement.leadingComments = [({ type: "BlockComment", value: leadingComment }: any)];
return statement;
}
let block = t.blockStatement(statements);
if (leadingComment.length > 0) {
block.leadingComments = [({ type: "BlockComment", value: leadingComment }: any)];
}
return block;
}
_serializeAssumeCall(data: OperationDescriptorData, [c, s]: Array<BabelNodeExpression>) {
let errorLiteral = s.type === "StringLiteral" ? s : t.stringLiteral("Assumption violated");
return t.ifStatement(
t.unaryExpression("!", c),
t.blockStatement([t.throwStatement(t.newExpression(t.identifier("Error"), [errorLiteral]))])
);
}
_serializeWidenAbstractPropertyAssignment(data: OperationDescriptorData, [o, p, v]: Array<BabelNodeExpression>) {
return t.assignmentExpression("=", memberExpressionHelper(o, p), v);
}
_serializeWidenPropertyAssignment({ propName }: OperationDescriptorData, [o, v]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return t.assignmentExpression("=", memberExpressionHelper(o, propName), v);
}
_serializeWidenAbstractProperty(data: OperationDescriptorData, [o, p]: Array<BabelNodeExpression>) {
return memberExpressionHelper(o, p);
}
_serializeWidenProperty({ propName }: OperationDescriptorData, [o]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return memberExpressionHelper(o, propName);
}
_serializeAbstractObjectGet(
{ propertyGetter, propName: P }: OperationDescriptorData,
[o]: Array<BabelNodeExpression>
) {
invariant(typeof P === "string");
return propertyGetter !== undefined
? t.callExpression(t.memberExpression(t.identifier("global"), t.identifier("__prop_" + propertyGetter)), [
o,
t.stringLiteral(P),
])
: memberExpressionHelper(o, P);
}
_serializeAbstractObjectGetProtoOf(data: OperationDescriptorData, [p]: Array<BabelNodeExpression>) {
invariant(this.realm.preludeGenerator !== undefined);
let getPrototypeOf = this.realm.preludeGenerator.memoizeReference("Object.getPrototypeOf");
return this.realm.isCompatibleWith(this.realm.MOBILE_JSC_VERSION) || this.realm.isCompatibleWith("mobile")
? t.memberExpression(p, protoExpression)
: t.callExpression(getPrototypeOf, [p]);
}
_serializeCannotBecomeObject(data: OperationDescriptorData, [n]: Array<BabelNodeExpression>) {
let callFunc = t.identifier("global.__cannotBecomeObject");
return t.callExpression(callFunc, [n]);
}
_serializeResidualCall(data: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
return t.callExpression(nodes[0], ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>));
}
_serializeModulesRequires({ propName }: OperationDescriptorData) {
invariant(propName !== undefined);
return t.callExpression(t.identifier("require"), [t.valueToNode(propName)]);
}
_serializeConcreteModel({ propName }: OperationDescriptorData, [valueNode]: Array<BabelNodeExpression>) {
invariant(propName !== undefined);
return t.expressionStatement(
t.assignmentExpression("=", this.preludeGenerator.globalReference(propName, false), valueNode)
);
}
_serializeConsoleLog({ propName }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
invariant(propName !== undefined);
return t.expressionStatement(
t.callExpression(t.memberExpression(t.identifier("console"), t.identifier(propName)), [...nodes])
);
}
_serializeDoWhile(
{ generator, value }: OperationDescriptorData,
nodes: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
) {
invariant(context !== undefined);
invariant(valuesToProcess !== undefined);
invariant(value !== undefined);
let testId = value.intrinsicName;
invariant(testId !== undefined);
invariant(generator !== undefined);
let statements = context.serializeGenerator(generator, valuesToProcess);
let block = t.blockStatement(statements);
return t.doWhileStatement(t.identifier(testId), block);
}
_serializeForIn({ boundName, lh }: OperationDescriptorData, [obj, tgt, src]: Array<BabelNodeExpression>) {
invariant(boundName !== undefined);
invariant(lh !== undefined);
return t.forInStatement(
lh,
obj,
t.blockStatement([
t.expressionStatement(
t.assignmentExpression("=", memberExpressionHelper(tgt, boundName), memberExpressionHelper(src, boundName))
),
])
);
}
_serializeFullInvariant({ propName }: OperationDescriptorData, [objectNode, valueNode]: Array<BabelNodeExpression>) {
invariant(propName !== undefined);
return t.binaryExpression("!==", memberExpressionHelper(objectNode, propName), valueNode);
}
_serializeFullInvariantFunction({ propName }: OperationDescriptorData, [objectNode]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return t.binaryExpression(
"!==",
t.unaryExpression("typeof", memberExpressionHelper(objectNode, propName), true),
t.stringLiteral("function")
);
}
_serializeFullInvariantAbstract(
{ concreteComparisons, typeComparisons }: OperationDescriptorData,
[valueNode]: Array<BabelNodeExpression>
) {
invariant(concreteComparisons !== undefined);
invariant(typeComparisons !== undefined);
// Create `object.property !== concreteValue`
let checks = concreteComparisons.map(concreteValue =>
t.binaryExpression("!==", valueNode, t.valueToNode(concreteValue.serialize()))
);
// Create `typeof object.property !== typeValue`
checks = checks.concat(
[...typeComparisons].map(typeValue => {
let typeString = Utils.typeToString(typeValue);
invariant(typeString !== undefined, typeValue);
return t.binaryExpression("!==", t.unaryExpression("typeof", valueNode, true), t.stringLiteral(typeString));
})
);
return checks.reduce((expr, newCondition) => t.logicalExpression("&&", expr, newCondition));
}
_serializeInvariantAppend({ propName }: OperationDescriptorData, [objectNode]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return memberExpressionHelper(objectNode, propName);
}
_serializePropertyInvariant({ propName, state }: OperationDescriptorData, [objectNode]: Array<BabelNodeExpression>) {
invariant(state !== undefined);
invariant(typeof propName === "string");
let n = t.callExpression(
t.memberExpression(
this.preludeGenerator.memoizeReference("Object.prototype.hasOwnProperty"),
t.identifier("call")
),
[objectNode, t.stringLiteral(propName)]
);
if (state !== "MISSING") {
n = t.unaryExpression("!", n, true);
if (state === "DEFINED")
n = t.logicalExpression(
"||",
n,
t.binaryExpression("===", memberExpressionHelper(objectNode, propName), t.valueToNode(undefined))
);
}
return n;
}
_serializeUpdateIncrementor({ op }: OperationDescriptorData, [oldValNode]: Array<BabelNodeExpression>) {
invariant(op !== undefined);
return t.binaryExpression(op, oldValNode, t.numericLiteral(1));
}
_serializeDerivedAbstractInvariant({ typeofString }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
invariant(typeofString !== undefined);
let condition = t.binaryExpression("!==", t.unaryExpression("typeof", nodes[0]), t.stringLiteral(typeofString));
if (typeofString === "object") {
condition = t.logicalExpression(
"&&",
condition,
t.binaryExpression("!==", t.unaryExpression("typeof", nodes[0]), t.stringLiteral("function"))
);
condition = t.logicalExpression("||", condition, t.binaryExpression("===", nodes[0], nullExpression));
}
return condition;
}
_serializeInvariant(
{ appendLastToInvariantOperationDescriptor, violationConditionOperationDescriptor }: OperationDescriptorData,
nodes: Array<BabelNodeExpression>
) {
invariant(violationConditionOperationDescriptor !== undefined);
let messageComponents = [
t.stringLiteral("Prepack model invariant violation ("),
t.numericLiteral(this.preludeGenerator.nextInvariantId++),
];
if (appendLastToInvariantOperationDescriptor) {
let last = nodes.pop();
messageComponents.push(t.stringLiteral("): "));
messageComponents.push(this.serialize(appendLastToInvariantOperationDescriptor, [last]));
} else {
messageComponents.push(t.stringLiteral(")"));
}
let throwString = messageComponents[0];
for (let i = 1; i < messageComponents.length; i++)
throwString = t.binaryExpression("+", throwString, messageComponents[i]);
let condition = this.serialize(violationConditionOperationDescriptor, nodes);
let consequent = this.getErrorStatement(throwString);
return t.ifStatement(condition, consequent);
}
_serializeReactRelayMockContainer(
{ propName }: OperationDescriptorData,
[reactRelayIdent, ...otherArgs]: Array<BabelNodeExpression>
) {
invariant(typeof propName === "string");
return t.callExpression(
t.memberExpression(reactRelayIdent, t.identifier(propName)),
((otherArgs: any): Array<any>)
);
}
_serializePropertyAssignment(
{ path }: OperationDescriptorData,
[o, p, v, e]: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
) {
invariant(path instanceof AbstractValue);
invariant(path.operationDescriptor !== undefined);
let lh = this.serialize(path.operationDescriptor, [o, p], context, valuesToProcess);
return t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
}
_serializeConditionalPropertyAssignment(
{ binding: _binding, path, value }: OperationDescriptorData,
[o, v, e]: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
) {
invariant(value instanceof AbstractValue);
invariant(path instanceof AbstractValue);
invariant(_binding !== undefined);
let binding = ((_binding: any): PropertyBinding);
invariant(value !== undefined);
let keyKey = binding.key;
invariant(typeof keyKey === "string");
let mightHaveBeenDeleted = value.mightHaveBeenDeleted();
let mightBeUndefined = value.mightBeUndefined();
invariant(path.operationDescriptor !== undefined);
let lh = this.serialize(path.operationDescriptor, [o, t.identifier(keyKey)], context, valuesToProcess);
let r = t.expressionStatement(t.assignmentExpression("=", (lh: any), v));
if (mightHaveBeenDeleted) {
// If v === __empty || (v === undefined && !(key.key in o)) then delete it
let emptyTest = t.binaryExpression("===", v, e);
let undefinedTest = t.binaryExpression("===", v, voidExpression);
let inTest = t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(keyKey), o));
let guard = t.logicalExpression("||", emptyTest, t.logicalExpression("&&", undefinedTest, inTest));
let deleteIt = t.expressionStatement(t.unaryExpression("delete", (lh: any)));
return t.ifStatement(mightBeUndefined ? emptyTest : guard, deleteIt, r);
}
return r;
}
_serializeLogicalPropertyAssignment(
{ binding: _binding, value }: OperationDescriptorData,
[o, n]: Array<BabelNodeExpression>
) {
invariant(value instanceof Value);
invariant(_binding !== undefined);
let binding = ((_binding: any): PropertyBinding);
if (typeof binding.key === "string" && value.mightHaveBeenDeleted() && isSelfReferential(value, binding.pathNode)) {
let inTest = t.binaryExpression("in", t.stringLiteral(binding.key), o);
let addEmpty = t.conditionalExpression(inTest, n, emptyExpression);
n = t.logicalExpression("||", n, addEmpty);
}
return n;
}
_serializeLocalAssignment(
{ value }: OperationDescriptorData,
[v]: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
) {
invariant(value instanceof AbstractValue);
invariant(value.operationDescriptor !== undefined);
let id = this.serialize(value.operationDescriptor, [], context, valuesToProcess);
return t.expressionStatement(t.assignmentExpression("=", (id: any), v));
}
_serializeReactNativeStringLiteral({ propName }: OperationDescriptorData) {
invariant(typeof propName === "string");
return t.stringLiteral(propName);
}
_serializeReactCreateContextProvider(data: OperationDescriptorData, [consumerNode]: Array<BabelNodeExpression>) {
return t.memberExpression(consumerNode, t.identifier("Provider"));
}
_serializeReactTemporalFunc(data: OperationDescriptorData, [renderNode, ..._args]: Array<BabelNodeExpression>) {
return t.callExpression(renderNode, ((_args: any): Array<any>));
}
_serializeCallAbstractFunc(data: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
let fun_args = ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(nodes[0], fun_args);
}
_serializeCallAbstractFuncThis(data: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
let fun_args = ((nodes.slice(1): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(t.memberExpression(nodes[0], t.identifier("call")), fun_args);
}
_serializeDirectCallWithArgList(data: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
let fun_args = nodes.slice(1);
return t.callExpression(nodes[0], ((fun_args: any): Array<BabelNodeExpression | BabelNodeSpreadElement>));
}
_serializeObjectProtoHasOwnProperty(
data: OperationDescriptorData,
[methodNode, objectNode, nameNode]: Array<BabelNodeExpression>
) {
return t.callExpression(t.memberExpression(methodNode, t.identifier("call")), [objectNode, nameNode]);
}
_serializeRebuiltObject({ propName }: OperationDescriptorData, [node]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return t.isValidIdentifier(propName)
? t.memberExpression(node, t.identifier(propName), false)
: t.memberExpression(node, t.stringLiteral(propName), true);
}
_serializeGlobalDelete({ propName }: OperationDescriptorData) {
invariant(typeof propName === "string");
return t.expressionStatement(t.unaryExpression("delete", this.preludeGenerator.globalReference(propName, false)));
}
_serializeDefineProperty(
{ object, propName, desc }: OperationDescriptorData,
args: Array<BabelNodeExpression>,
context?: SerializationContext
) {
invariant(object !== undefined);
invariant(propName !== undefined);
invariant(desc !== undefined);
invariant(context !== undefined);
return context.emitDefinePropertyBody(object, propName, desc);
}
_serializeFBMocksMagicGlobalFunction({ propName }: OperationDescriptorData, args: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return t.callExpression(t.identifier(propName), ((args: any): Array<any>));
}
_serializeFBMocksBootloaderLoadModules(data: OperationDescriptorData, args: Array<BabelNodeExpression>) {
return t.callExpression(
t.memberExpression(t.identifier("Bootloader"), t.identifier("loadModules")),
((args: any): Array<any>)
);
}
_serializeAbstractObjectSetPartial(
{ propName }: OperationDescriptorData,
[objectNode, valueNode]: Array<BabelNodeExpression>
) {
invariant(typeof propName === "string");
return t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, propName), valueNode));
}
_serializeAbstractObjectSetPartialValue(
data: OperationDescriptorData,
[objectNode, keyNode, valueNode]: Array<BabelNodeExpression>
) {
return t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, keyNode), valueNode));
}
_serializeUnknownArrayGetPartial(data: OperationDescriptorData, [o, p]: Array<BabelNodeExpression>) {
return memberExpressionHelper(o, p);
}
_serializeObjectGetPartial(data: OperationDescriptorData, [o, p]: Array<BabelNodeExpression>) {
return memberExpressionHelper(o, p);
}
_serializeAbstractObjectGetPartial(data: OperationDescriptorData, [o, p]: Array<BabelNodeExpression>) {
return memberExpressionHelper(o, p);
}
_serializeObjectSetPartial(
data: OperationDescriptorData,
[objectNode, keyNode, valueNode]: Array<BabelNodeExpression>
) {
return t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, keyNode), valueNode));
}
_serializeIdentifier(data: OperationDescriptorData) {
invariant(typeof data.id === "string");
return t.identifier(data.id);
}
_serializeCoerceToString(data: OperationDescriptorData, [p]: Array<BabelNodeExpression>) {
return t.binaryExpression("+", t.stringLiteral(""), p);
}
_serializeBabelHelpersObjectWithoutProperties(
data: OperationDescriptorData,
[methodNode, objNode, propRemoveNode]: Array<BabelNodeExpression>
) {
return t.callExpression(methodNode, [objNode, propRemoveNode]);
}
_serializeReactDefaultPropsHelper(data: OperationDescriptorData, [methodNode, ..._args]: Array<BabelNodeExpression>) {
return t.callExpression(methodNode, ((_args: any): Array<any>));
}
_serializeUnknownArrayMethodCall(data: OperationDescriptorData, [methodNode, ..._args]: Array<BabelNodeExpression>) {
return t.callExpression(methodNode, ((_args: any): Array<any>));
}
_serializeUnknownArrayLength(data: OperationDescriptorData, [o]: Array<BabelNodeExpression>) {
return t.memberExpression(o, t.identifier("length"), false);
}
_serializeUnknownArrayMethodPropertyCall(
{ propName }: OperationDescriptorData,
[objNode, ..._args]: Array<BabelNodeExpression>
) {
invariant(typeof propName === "string");
return t.callExpression(t.memberExpression(objNode, t.identifier(propName)), ((_args: any): Array<any>));
}
_serializeThrow(data: OperationDescriptorData, [argument]: Array<BabelNodeExpression>) {
return t.throwStatement(argument);
}
_serializeConditionalThrow(
{ value }: OperationDescriptorData,
nodes: Array<BabelNodeExpression>,
context?: SerializationContext
) {
invariant(value instanceof Value);
function createStatement(val: Value) {
invariant(context !== undefined);
if (!(val instanceof AbstractValue) || val.kind !== "conditional") {
return t.throwStatement(context.serializeValue(val));
}
let [cond, trueVal, falseVal] = val.args;
let condVal = context.serializeValue(cond);
let trueStat, falseStat;
if (trueVal instanceof EmptyValue) trueStat = t.blockStatement([]);
else trueStat = createStatement(trueVal);
if (falseVal instanceof EmptyValue) falseStat = t.blockStatement([]);
else falseStat = createStatement(falseVal);
return t.ifStatement(condVal, trueStat, falseStat);
}
return createStatement(value);
}
_serializeReactSSRTemplateLiteral({ quasis }: OperationDescriptorData, valueNodes: Array<BabelNodeExpression>) {
invariant(quasis !== undefined);
return t.templateLiteral(((quasis: any): Array<any>), valueNodes);
}
_serializeReactRenderValueHelper(data: OperationDescriptorData, [helperNode, valueNode]: Array<BabelNodeExpression>) {
return t.callExpression(helperNode, [valueNode]);
}
_serializePropertyDelete({ propName }: OperationDescriptorData, [objectNode]: Array<BabelNodeExpression>) {
invariant(propName !== undefined);
return t.expressionStatement(t.unaryExpression("delete", memberExpressionHelper(objectNode, propName)));
}
_serializeGetBinding(
{ binding }: OperationDescriptorData,
nodes: Array<BabelNodeExpression>,
context?: SerializationContext
) {
invariant(binding !== undefined);
invariant(context !== undefined);
return context.serializeBinding(((binding: any): Binding));
}
_serializeForFunctionCall(data: OperationDescriptorData, [func, thisExpr]: Array<BabelNodeExpression>) {
let { usesThis } = data;
return usesThis
? t.callExpression(t.memberExpression(func, t.identifier("call")), [thisExpr])
: t.callExpression(func, []);
}
_serializeNewExpression(
data: OperationDescriptorData,
[constructorNode, ...argListNodes]: Array<BabelNodeExpression>
) {
return t.newExpression(constructorNode, argListNodes);
}
_serializeEmitCall({ callTemplate }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
invariant(typeof callTemplate === "function");
return t.expressionStatement(t.callExpression(callTemplate(), [...nodes]));
}
_serializeEmitCallAndCaptureResults({ callTemplate }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
invariant(typeof callTemplate === "function");
return t.callExpression(callTemplate(), ((nodes: any): Array<BabelNodeExpression | BabelNodeSpreadElement>));
}
_serializeObjectProtoGetOwnPropertyDescriptor(
data: OperationDescriptorData,
[funcNode, ...args]: Array<BabelNodeExpression>
) {
return t.callExpression(funcNode, ((args: any): Array<BabelNodeExpression | BabelNodeSpreadElement>));
}
_serializeCallBailout({ propRef, thisArg }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
let callFunc;
let argStart = 1;
if (thisArg instanceof Value) {
if (typeof propRef === "string") {
callFunc = memberExpressionHelper(nodes[0], propRef);
} else {
callFunc = memberExpressionHelper(nodes[0], nodes[1]);
argStart = 2;
}
} else {
callFunc = nodes[0];
}
let fun_args = ((nodes.slice(argStart): any): Array<BabelNodeExpression | BabelNodeSpreadElement>);
return t.callExpression(callFunc, fun_args);
}
_serializeJoinGenerators(
{ generators }: OperationDescriptorData,
[cond]: Array<BabelNodeExpression>,
context?: SerializationContext,
valuesToProcess?: Set<AbstractValue | ObjectValue>
): BabelNodeStatement {
invariant(context !== undefined);
invariant(valuesToProcess !== undefined);
invariant(generators !== undefined);
let [generator1, generator2] = generators;
let block1 = generator1.empty() ? null : serializeBody(generator1, context, valuesToProcess);
let block2 = generator2.empty() ? null : serializeBody(generator2, context, valuesToProcess);
if (block1) return t.ifStatement(cond, block1, block2);
invariant(block2);
return t.ifStatement(t.unaryExpression("!", cond), block2);
}
_serializeEmitPropertyAssignment(
{ propName, value }: OperationDescriptorData,
[objectNode, valueNode]: Array<BabelNodeExpression>,
context?: SerializationContext
) {
invariant(context !== undefined);
invariant(value instanceof Value);
invariant(typeof propName === "string");
return context.getPropertyAssignmentStatement(
memberExpressionHelper(objectNode, propName),
value,
value.mightHaveBeenDeleted(),
/* deleteIfMightHaveBeenDeleted */ true
);
}
_serializeGlobalAssignment({ propName }: OperationDescriptorData, [valueNode]: Array<BabelNodeExpression>) {
invariant(typeof propName === "string");
return t.expressionStatement(
t.assignmentExpression("=", this.preludeGenerator.globalReference(propName, false), valueNode)
);
}
_serializeSingleArg(data: OperationDescriptorData, [o]: Array<BabelNodeExpression>) {
return o;
}
_serializeAbstractProperty({ propName }: OperationDescriptorData, [o]: Array<BabelNodeExpression>) {
invariant(propName !== undefined);
return memberExpressionHelper(o, propName);
}
_serializeUnaryExpression({ op, prefix }: OperationDescriptorData, [x, y]: Array<BabelNodeExpression>) {
invariant(op !== undefined);
return t.unaryExpression(op, x, prefix);
}
_serializeBinaryExpression({ op }: OperationDescriptorData, [x, y]: Array<BabelNodeExpression>) {
invariant(op !== undefined);
return t.binaryExpression(op, x, y);
}
_serializeLogicalExpression({ op }: OperationDescriptorData, [x, y]: Array<BabelNodeExpression>) {
invariant(op !== undefined);
return t.logicalExpression(op, x, y);
}
_serializeConditionalExpression(data: OperationDescriptorData, [c, x, y]: Array<BabelNodeExpression>) {
return t.conditionalExpression(c, x, y);
}
_serializeDerivedOperationDescriptor(
data: OperationDescriptorData,
babelNode: BabelNodeExpression
): BabelNodeStatement {
invariant(typeof data.id === "string");
return t.variableDeclaration("var", [t.variableDeclarator(t.identifier(data.id), babelNode)]);
}
_serializeVoidOperationDescriptor(babelNode: BabelNodeExpression) {
return t.expressionStatement(babelNode);
}
_serializeAbstractFromTemplate({ template }: OperationDescriptorData, nodes: Array<BabelNodeExpression>) {
let generatorArgs = {};
let i = 0;
for (let node of nodes) generatorArgs[labels.charAt(i++)] = node;
invariant(typeof template === "function");
return template(this.preludeGenerator)(generatorArgs);
}
_serializeObjectAssign(data: OperationDescriptorData, [targetNode, ...sourceNodes]: Array<BabelNodeExpression>) {
return t.callExpression(this.preludeGenerator.memoizeReference("Object.assign"), [targetNode, ...sourceNodes]);
}
}

View File

@ -18,7 +18,7 @@ import { AbstractValue, AbstractObjectValue, ObjectValue, SymbolValue, Value } f
import { convertExpressionToJSXIdentifier, convertKeyValueToJSXAttribute } from "../react/jsx.js";
import { Logger } from "../utils/logger.js";
import invariant from "../invariant.js";
import { FatalError } from "../errors";
import { FatalError } from "../errors.js";
import { traverseReactElement } from "../react/elements.js";
import { canExcludeReactElementObjectProperty, getReactSymbol, getProperty } from "../react/utils.js";
import type { ReactOutputTypes } from "../options.js";

View File

@ -28,7 +28,7 @@ import { Logger } from "../utils/logger.js";
import { Generator } from "../utils/generator.js";
import type { AdditionalFunctionEffects } from "./types";
import type { Binding } from "../environment.js";
import { getLocationFromValue } from "../react/utils";
import { getLocationFromValue } from "../react/utils.js";
/**
* Get index property list length by searching array properties list for the max index key value plus 1.

View File

@ -157,7 +157,7 @@ export type PropertyBinding = {
descriptor?: Descriptor,
object: ObjectValue,
key: void | string | SymbolValue | AbstractValue, // where an abstract value must be of type String or Number or Symbol
// contains a build node that produces a member expression that resolves to this property binding (location)
// contains a operation descriptor that produces a member expression that resolves to this property binding (location)
pathNode?: AbstractValue,
internalSlot?: boolean,
};

View File

@ -28,7 +28,7 @@ import {
} from "../values/index.js";
import { To } from "../singletons.js";
import invariant from "../invariant.js";
import { Logger } from "../utils/logger.js";
import { Logger } from "./logger.js";
type TargetIntegrityCommand = "freeze" | "seal" | "preventExtensions" | "";

View File

@ -9,16 +9,21 @@
/* @flow */
import type { Realm, Effects } from "../realm.js";
import type { ConsoleMethodTypes, Descriptor, PropertyBinding, DisplayResult } from "../types.js";
import type { Binding } from "../environment.js";
import type { Effects, Realm } from "../realm.js";
import type {
ConsoleMethodTypes,
Descriptor,
DisplayResult,
PropertyBinding,
SupportedGraphQLGetters,
} from "../types.js";
import type { BaseValue, Binding, ReferenceName } from "../environment.js";
import {
AbstractObjectValue,
AbstractValue,
type AbstractValueKind,
BooleanValue,
ConcreteValue,
EmptyValue,
FunctionValue,
NullValue,
NumberValue,
@ -30,9 +35,7 @@ import {
Value,
} from "../values/index.js";
import { CompilerDiagnostic } from "../errors.js";
import type { AbstractValueBuildNodeFunction } from "../values/AbstractValue.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import * as t from "@babel/types";
import invariant from "../invariant.js";
import {
AbruptCompletion,
@ -52,12 +55,159 @@ import type {
BabelNodeBlockStatement,
BabelNodeLVal,
} from "@babel/types";
import { nullExpression, memberExpressionHelper } from "./babelhelpers.js";
import { Utils, concretize } from "../singletons.js";
import { memberExpressionHelper } from "./babelhelpers.js";
import { concretize, Utils } from "../singletons.js";
import type { SerializerOptions } from "../options.js";
import * as t from "@babel/types";
export type OperationDescriptorType =
| "IDENTIFIER"
| "SINGLE_ARG"
| "REBUILT_OBJECT"
| "CONSOLE_LOG"
| "FOR_IN"
| "DO_WHILE"
| "CONCRETE_MODEL"
| "BINARY_EXPRESSION"
| "LOGICAL_EXPRESSION"
| "CONDITIONAL_EXPRESSION"
| "UNARY_EXPRESSION"
| "ABSTRACT_FROM_TEMPLATE"
| "GLOBAL_ASSIGNMENT"
| "GLOBAL_DELETE"
| "EMIT_PROPERTY_ASSIGNMENT"
| "ABSTRACT_PROPERTY"
| "JOIN_GENERATORS"
| "APPEND_GENERATOR"
| "DEFINE_PROPERTY"
| "PROPERTY_DELETE"
| "THROW"
| "CONDITIONAL_THROW"
| "COERCE_TO_STRING"
| "ABSTRACT_FROM_TEMPLATE"
| "FOR_STATEMENT_FUNC"
| "NEW_EXPRESSION"
| "OBJECT_ASSIGN"
| "OBJECT_SET_PARTIAL"
| "OBJECT_GET_PARTIAL"
| "OBJECT_PROTO_HAS_OWN_PROPERTY"
| "OBJECT_PROTO_GET_OWN_PROPERTY_DESCRIPTOR"
| "ABSTRACT_OBJECT_SET_PARTIAL"
| "ABSTRACT_OBJECT_SET_PARTIAL_VALUE"
| "ABSTRACT_OBJECT_GET_PARTIAL"
| "ABSTRACT_OBJECT_GET_PROTO_OF"
| "ABSTRACT_OBJECT_GET"
| "DIRECT_CALL_WITH_ARG_LIST"
| "CALL_ABSTRACT_FUNC"
| "CALL_ABSTRACT_FUNC_THIS"
| "CALL_BAILOUT"
| "EMIT_CALL"
| "EMIT_CALL_AND_CAPTURE_RESULT"
| "GET_BINDING"
| "LOCAL_ASSIGNMENT"
| "LOGICAL_PROPERTY_ASSIGNMENT"
| "CONDITIONAL_PROPERTY_ASSIGNMENT"
| "PROPERTY_ASSIGNMENT"
| "MODULES_REQUIRE"
| "RESIDUAL_CALL"
| "ASSUME_CALL"
| "CANNOT_BECOME_OBJECT"
| "UPDATE_INCREMENTOR"
| "WIDENED_IDENTIFIER"
| "WIDEN_PROPERTY"
| "WIDEN_ABSTRACT_PROPERTY"
| "WIDEN_PROPERTY_ASSIGNMENT"
| "WIDEN_ABSTRACT_PROPERTY_ASSIGNMENT"
| "INVARIANT"
| "INVARIANT_APPEND"
| "DERIVED_ABSTRACT_INVARIANT"
| "PROPERTY_INVARIANT"
| "FULL_INVARIANT"
| "FULL_INVARIANT_FUNCTION"
| "FULL_INVARIANT_ABSTRACT"
| "UNKNOWN_ARRAY_METHOD_CALL"
| "UNKNOWN_ARRAY_METHOD_PROPERTY_CALL"
| "UNKNOWN_ARRAY_LENGTH"
| "UNKNOWN_ARRAY_GET_PARTIAL"
| "BABEL_HELPERS_OBJECT_WITHOUT_PROPERTIES"
| "REACT_DEFAULT_PROPS_HELPER"
| "REACT_TEMPORAL_FUNC"
| "REACT_CREATE_CONTEXT_PROVIDER"
| "REACT_SSR_REGEX_CONSTANT"
| "REACT_SSR_PREV_TEXT_NODE"
| "REACT_SSR_RENDER_VALUE_HELPER"
| "REACT_SSR_TEMPLATE_LITERAL"
| "REACT_NATIVE_STRING_LITERAL"
| "REACT_RELAY_MOCK_CONTAINER"
| "FB_MOCKS_BOOTLOADER_LOAD_MODULES"
| "FB_MOCKS_MAGIC_GLOBAL_FUNCTION";
export type DerivedExpressionBuildNodeFunction = (
Array<BabelNodeExpression>,
SerializationContext,
Set<AbstractValue | ObjectValue>
) => BabelNodeExpression;
export type OperationDescriptor = {
data: OperationDescriptorData,
kind: void | ResidualBuildKind,
type: OperationDescriptorType,
};
// TODO: gradually remove all these, currently it's a random bag of values
// that should be in args or in other places rather than here.
export type OperationDescriptorData = {
appendLastToInvariantOperationDescriptor?: OperationDescriptor,
binding?: Binding | PropertyBinding,
boundName?: BabelNodeIdentifier,
callTemplate?: () => BabelNodeExpression,
concreteComparisons?: Array<Value>,
desc?: Descriptor,
generator?: Generator,
generators?: Array<Generator>,
id?: string,
lh?: BabelNodeVariableDeclaration,
op?: any, // TODO: This is a union of Babel operators, refactor to not use "any" at some point
prefix?: boolean,
path?: Value,
propertyGetter?: SupportedGraphQLGetters,
propName?: string,
propRef?: ReferenceName | AbstractValue,
object?: ObjectValue,
quasis?: Array<any>,
state?: "MISSING" | "PRESENT" | "DEFINED",
thisArg?: BaseValue | Value,
template?: PreludeGenerator => ({}) => BabelNodeExpression,
typeComparisons?: Set<typeof Value>,
typeofString?: string,
usesThis?: boolean,
value?: Value,
violationConditionOperationDescriptor?: OperationDescriptor,
};
export type ResidualBuildKind = "DERIVED" | "VOID";
export function createOperationDescriptor(
type: OperationDescriptorType,
data?: OperationDescriptorData = {},
kind?: ResidualBuildKind
): OperationDescriptor {
return {
data,
kind,
type,
};
}
import type { ShapeInformationInterface } from "../types.js";
export type SerializationContext = {|
serializeOperationDescriptor: (
OperationDescriptor,
Array<BabelNodeExpression>,
SerializationContext,
Set<AbstractValue | ObjectValue>
) => BabelNodeStatement,
serializeValue: Value => BabelNodeExpression,
serializeBinding: Binding => BabelNodeIdentifier | BabelNodeMemberExpression,
getPropertyAssignmentStatement: (
@ -90,20 +240,6 @@ export type VisitEntryCallbacks = {|
visitBindingAssignment: (Binding, Value) => Value,
|};
export type TemporalBuildNodeType = "OBJECT_ASSIGN";
export type DerivedExpressionBuildNodeFunction = (
Array<BabelNodeExpression>,
SerializationContext,
Set<AbstractValue | ObjectValue>
) => BabelNodeExpression;
export type GeneratorBuildNodeFunction = (
Array<BabelNodeExpression>,
SerializationContext,
Set<AbstractValue | ObjectValue>
) => BabelNodeStatement;
export class GeneratorEntry {
constructor(realm: Realm) {
// We increment the index of every TemporalBuildNodeEntry created.
@ -141,12 +277,11 @@ export class GeneratorEntry {
export type TemporalBuildNodeEntryArgs = {
declared?: AbstractValue | ObjectValue,
args: Array<Value>,
// If we're just trying to add roots for the serializer to notice, we don't need a buildNode.
buildNode?: GeneratorBuildNodeFunction,
// If we're just trying to add roots for the serializer to notice, we don't need an operationDescriptor.
operationDescriptor?: OperationDescriptor,
dependencies?: Array<Generator>,
isPure?: boolean,
mutatesOnly?: Array<Value>,
temporalType?: TemporalBuildNodeType,
};
export class TemporalBuildNodeEntry extends GeneratorEntry {
@ -163,17 +298,16 @@ export class TemporalBuildNodeEntry extends GeneratorEntry {
declared: void | AbstractValue | ObjectValue;
args: Array<Value>;
// If we're just trying to add roots for the serializer to notice, we don't need a buildNode.
buildNode: void | GeneratorBuildNodeFunction;
// If we're just trying to add roots for the serializer to notice, we don't need an operationDescriptor.
operationDescriptor: void | OperationDescriptor;
dependencies: void | Array<Generator>;
isPure: void | boolean;
mutatesOnly: void | Array<Value>;
temporalType: void | TemporalBuildNodeType;
toDisplayJson(depth: number): DisplayResult {
if (depth <= 0) return `TemporalBuildNode${this.index}`;
let obj = { type: "TemporalBuildNode", ...this };
delete obj.buildNode;
delete obj.operationDescriptor;
return Utils.verboseToDisplayJson(obj, depth);
}
@ -213,9 +347,9 @@ export class TemporalBuildNodeEntry extends GeneratorEntry {
}
if (!omit) {
let nodes = this.args.map((boundArg, i) => context.serializeValue(boundArg));
if (this.buildNode) {
if (this.operationDescriptor !== undefined) {
let valuesToProcess = new Set();
let node = this.buildNode(nodes, context, valuesToProcess);
let node = context.serializeOperationDescriptor(this.operationDescriptor, nodes, context, valuesToProcess);
if (node.type === "BlockStatement") {
let block: BabelNodeBlockStatement = (node: any);
let statements = block.body;
@ -476,16 +610,6 @@ class BindingAssignmentEntry extends GeneratorEntry {
}
}
function serializeBody(
generator: Generator,
context: SerializationContext,
valuesToProcess: Set<AbstractValue | ObjectValue>
): BabelNodeBlockStatement {
let statements = context.serializeGenerator(generator, valuesToProcess);
if (statements.length === 1 && statements[0].type === "BlockStatement") return (statements[0]: any);
return t.blockStatement(statements);
}
export class Generator {
constructor(realm: Realm, name: string, pathConditions: Array<AbstractValue>, effects?: Effects) {
invariant(realm.useAbstractInterpretation);
@ -642,28 +766,21 @@ export class Generator {
emitGlobalAssignment(key: string, value: Value): void {
this._addEntry({
args: [value],
buildNode: ([valueNode]) =>
t.expressionStatement(
t.assignmentExpression("=", this.preludeGenerator.globalReference(key, false), valueNode)
),
operationDescriptor: createOperationDescriptor("GLOBAL_ASSIGNMENT", { propName: key }),
});
}
emitConcreteModel(key: string, value: Value): void {
this._addEntry({
args: [concretize(this.realm, value)],
buildNode: ([valueNode]) =>
t.expressionStatement(
t.assignmentExpression("=", this.preludeGenerator.globalReference(key, false), valueNode)
),
operationDescriptor: createOperationDescriptor("CONCRETE_MODEL", { propName: key }),
});
}
emitGlobalDelete(key: string): void {
this._addEntry({
args: [],
buildNode: ([]) =>
t.expressionStatement(t.unaryExpression("delete", this.preludeGenerator.globalReference(key, false))),
operationDescriptor: createOperationDescriptor("GLOBAL_DELETE", { propName: key }),
});
}
@ -675,13 +792,7 @@ export class Generator {
if (object.refuseSerialization) return;
this._addEntry({
args: [object, value],
buildNode: ([objectNode, valueNode], context) =>
context.getPropertyAssignmentStatement(
memberExpressionHelper(objectNode, key),
value,
value.mightHaveBeenDeleted(),
/* deleteIfMightHaveBeenDeleted */ true
),
operationDescriptor: createOperationDescriptor("EMIT_PROPERTY_ASSIGNMENT", { propName: key, value }),
});
}
@ -702,7 +813,7 @@ export class Generator {
desc.get || object.$Realm.intrinsics.undefined,
desc.set || object.$Realm.intrinsics.undefined,
],
buildNode: (_, context: SerializationContext) => context.emitDefinePropertyBody(object, key, desc),
operationDescriptor: createOperationDescriptor("DEFINE_PROPERTY", { object, propName: key, desc }),
});
}
}
@ -711,59 +822,37 @@ export class Generator {
if (object.refuseSerialization) return;
this._addEntry({
args: [object],
buildNode: ([objectNode]) =>
t.expressionStatement(t.unaryExpression("delete", memberExpressionHelper(objectNode, key))),
operationDescriptor: createOperationDescriptor("PROPERTY_DELETE", { propName: key }),
});
}
emitCall(createCallee: () => BabelNodeExpression, args: Array<Value>): void {
emitCall(callTemplate: () => BabelNodeExpression, args: Array<Value>): void {
this._addEntry({
args,
buildNode: values => t.expressionStatement(t.callExpression(createCallee(), [...values])),
operationDescriptor: createOperationDescriptor("EMIT_CALL", { callTemplate }),
});
}
emitConsoleLog(method: ConsoleMethodTypes, args: Array<string | ConcreteValue>): void {
this.emitCall(
() => t.memberExpression(t.identifier("console"), t.identifier(method)),
args.map(v => (typeof v === "string" ? new StringValue(this.realm, v) : v))
);
this._addEntry({
args: args.map(v => (typeof v === "string" ? new StringValue(this.realm, v) : v)),
operationDescriptor: createOperationDescriptor("CONSOLE_LOG", { propName: method }),
});
}
// test must be a temporal value, which means that it must have a defined intrinsicName
emitDoWhileStatement(test: AbstractValue, body: Generator): void {
this._addEntry({
args: [],
buildNode: function([], context, valuesToProcess) {
let testId = test.intrinsicName;
invariant(testId !== undefined);
let statements = context.serializeGenerator(body, valuesToProcess);
let block = t.blockStatement(statements);
return t.doWhileStatement(t.identifier(testId), block);
},
operationDescriptor: createOperationDescriptor("DO_WHILE", { generator: body, value: test }),
dependencies: [body],
});
}
emitConditionalThrow(value: Value): void {
function createStatement(val: Value, context: SerializationContext) {
if (!(val instanceof AbstractValue) || val.kind !== "conditional") {
return t.throwStatement(context.serializeValue(val));
}
let [cond, trueVal, falseVal] = val.args;
let condVal = context.serializeValue(cond);
let trueStat, falseStat;
if (trueVal instanceof EmptyValue) trueStat = t.blockStatement([]);
else trueStat = createStatement(trueVal, context);
if (falseVal instanceof EmptyValue) falseStat = t.blockStatement([]);
else falseStat = createStatement(falseVal, context);
return t.ifStatement(condVal, trueStat, falseStat);
}
this._addEntry({
args: [value],
buildNode: function([argument], context: SerializationContext) {
return createStatement(value, context);
},
operationDescriptor: createOperationDescriptor("CONDITIONAL_THROW", { value }),
});
}
@ -784,7 +873,7 @@ export class Generator {
emitThrow(value: Value): void {
this._issueThrowCompilerDiagnostic(value);
this.emitStatement([value], ([argument]) => t.throwStatement(argument));
this.emitStatement([value], createOperationDescriptor("THROW"));
}
// Checks the full set of possible concrete values as well as typeof
@ -793,8 +882,6 @@ export class Generator {
// NB: if the type of the AbstractValue is top, skips the invariant
emitFullInvariant(object: ObjectValue | AbstractObjectValue, key: string, value: Value): void {
if (object.refuseSerialization) return;
let accessedPropertyOf = objectNode => memberExpressionHelper(objectNode, key);
let condition;
if (value instanceof AbstractValue) {
let isTop = false;
let concreteComparisons = [];
@ -823,58 +910,27 @@ export class Generator {
if (isTop) {
return;
} else {
condition = ([valueNode]) => {
// Create `object.property !== concreteValue`
let checks = concreteComparisons.map(concreteValue =>
t.binaryExpression("!==", valueNode, t.valueToNode(concreteValue.serialize()))
);
// Create `typeof object.property !== typeValue`
checks = checks.concat(
[...typeComparisons].map(typeValue => {
let typeString = Utils.typeToString(typeValue);
invariant(typeString !== undefined, typeValue);
return t.binaryExpression(
"!==",
t.unaryExpression("typeof", valueNode, true),
t.stringLiteral(typeString)
);
})
);
return checks.reduce((expr, newCondition) => t.logicalExpression("&&", expr, newCondition));
};
this._emitInvariant([value, value], condition, valueNode => valueNode);
this._emitInvariant(
[value, value],
createOperationDescriptor("FULL_INVARIANT_ABSTRACT", { concreteComparisons, typeComparisons }),
createOperationDescriptor("INVARIANT_APPEND", { propName: key })
);
}
} else if (value instanceof FunctionValue) {
// We do a special case for functions,
// as we like to use concrete functions in the model to model abstract behaviors.
// These concrete functions do not have the right identity.
condition = ([objectNode]) =>
t.binaryExpression(
"!==",
t.unaryExpression("typeof", accessedPropertyOf(objectNode), true),
t.stringLiteral("function")
);
this._emitInvariant([object, value, object], condition, objnode => accessedPropertyOf(objnode));
this._emitInvariant(
[object, value, object],
createOperationDescriptor("FULL_INVARIANT_FUNCTION", { propName: key }),
createOperationDescriptor("INVARIANT_APPEND", { propName: key })
);
} else {
condition = ([objectNode, valueNode]) => t.binaryExpression("!==", accessedPropertyOf(objectNode), valueNode);
this._emitInvariant([object, value, object], condition, objnode => accessedPropertyOf(objnode));
}
}
getErrorStatement(message: BabelNodeExpression): BabelNodeStatement {
if (this.realm.invariantMode === "throw")
return t.throwStatement(t.newExpression(this.preludeGenerator.memoizeReference("Error"), [message]));
else {
let targetReference = this.realm.invariantMode;
let args = [message];
let i = targetReference.indexOf("+");
if (i !== -1) {
let s = targetReference.substr(i + 1);
let x = Number.parseInt(s, 10);
args.push(isNaN(x) ? t.stringLiteral(s) : t.numericLiteral(x));
targetReference = targetReference.substr(0, i);
}
return t.expressionStatement(t.callExpression(this.preludeGenerator.memoizeReference(targetReference), args));
this._emitInvariant(
[object, value, object],
createOperationDescriptor("FULL_INVARIANT", { propName: key }),
createOperationDescriptor("INVARIANT_APPEND", { propName: key })
);
}
}
@ -884,72 +940,50 @@ export class Generator {
state: "MISSING" | "PRESENT" | "DEFINED"
): void {
if (object.refuseSerialization) return;
let accessedPropertyOf = (objectNode: BabelNodeExpression) => memberExpressionHelper(objectNode, key);
let condition = ([objectNode: BabelNodeExpression]) => {
let n = t.callExpression(
t.memberExpression(
this.preludeGenerator.memoizeReference("Object.prototype.hasOwnProperty"),
t.identifier("call")
),
[objectNode, t.stringLiteral(key)]
);
if (state !== "MISSING") {
n = t.unaryExpression("!", n, true);
if (state === "DEFINED")
n = t.logicalExpression(
"||",
n,
t.binaryExpression("===", accessedPropertyOf(objectNode), t.valueToNode(undefined))
);
}
return n;
};
this._emitInvariant([object, object], condition, objnode => accessedPropertyOf(objnode));
this._emitInvariant(
[object, object],
createOperationDescriptor("PROPERTY_INVARIANT", { state, propName: key }),
createOperationDescriptor("INVARIANT_APPEND", { propName: key })
);
}
_emitInvariant(
args: Array<Value>,
violationConditionFn: (Array<BabelNodeExpression>) => BabelNodeExpression,
appendLastToInvariantFn?: BabelNodeExpression => BabelNodeExpression
violationConditionOperationDescriptor: OperationDescriptor,
appendLastToInvariantOperationDescriptor: OperationDescriptor
): void {
invariant(this.realm.invariantLevel > 0);
let invariantOperationDescriptor = createOperationDescriptor("INVARIANT", {
appendLastToInvariantOperationDescriptor,
violationConditionOperationDescriptor,
});
this._addEntry({
args,
buildNode: (nodes: Array<BabelNodeExpression>) => {
let messageComponents = [
t.stringLiteral("Prepack model invariant violation ("),
t.numericLiteral(this.preludeGenerator.nextInvariantId++),
];
if (appendLastToInvariantFn) {
let last = nodes.pop();
messageComponents.push(t.stringLiteral("): "));
messageComponents.push(appendLastToInvariantFn(last));
} else messageComponents.push(t.stringLiteral(")"));
let throwString = messageComponents[0];
for (let i = 1; i < messageComponents.length; i++)
throwString = t.binaryExpression("+", throwString, messageComponents[i]);
let condition = violationConditionFn(nodes);
let consequent = this.getErrorStatement(throwString);
return t.ifStatement(condition, consequent);
},
operationDescriptor: invariantOperationDescriptor,
});
}
emitCallAndCaptureResult(
types: TypesDomain,
values: ValuesDomain,
createCallee: () => BabelNodeExpression,
callTemplate: () => BabelNodeExpression,
args: Array<Value>,
kind?: AbstractValueKind
): AbstractValue {
return this.deriveAbstract(types, values, args, (nodes: any) => t.callExpression(createCallee(), nodes), { kind });
return this.deriveAbstract(
types,
values,
args,
createOperationDescriptor("EMIT_CALL_AND_CAPTURE_RESULT", { callTemplate }),
{ kind }
);
}
emitStatement(args: Array<Value>, buildNode_: (Array<BabelNodeExpression>) => BabelNodeStatement): void {
emitStatement(args: Array<Value>, operationDescriptor: OperationDescriptor): void {
invariant(typeof operationDescriptor !== "function");
this._addEntry({
args,
buildNode: buildNode_,
operationDescriptor,
});
}
@ -957,16 +991,12 @@ export class Generator {
types: TypesDomain,
values: ValuesDomain,
args: Array<Value>,
buildNode_: AbstractValueBuildNodeFunction | BabelNodeExpression
operationDescriptor: OperationDescriptor
): UndefinedValue {
let voidOperationDescriptor = createOperationDescriptor(operationDescriptor.type, operationDescriptor.data, "VOID");
this._addEntry({
args,
buildNode: (nodes: Array<BabelNodeExpression>) =>
t.expressionStatement(
(buildNode_: any) instanceof Function
? ((buildNode_: any): AbstractValueBuildNodeFunction)(nodes)
: ((buildNode_: any): BabelNodeExpression)
),
operationDescriptor: voidOperationDescriptor,
});
return this.realm.intrinsics.undefined;
}
@ -981,49 +1011,31 @@ export class Generator {
this._addEntry({
// duplicate args to ensure refcount > 1
args: [o, targetObject, sourceObject, targetObject, sourceObject],
buildNode: ([obj, tgt, src, obj1, tgt1, src1]) => {
return t.forInStatement(
lh,
obj,
t.blockStatement([
t.expressionStatement(
t.assignmentExpression(
"=",
memberExpressionHelper(tgt, boundName),
memberExpressionHelper(src, boundName)
)
),
])
);
},
operationDescriptor: createOperationDescriptor("FOR_IN", { boundName, lh }),
});
}
deriveConcreteObject(
buildValue: (intrinsicName: string) => ObjectValue,
args: Array<Value>,
buildNode_: DerivedExpressionBuildNodeFunction | BabelNodeExpression,
operationDescriptor: OperationDescriptor,
optionalArgs?: {| isPure?: boolean |}
): ConcreteValue {
invariant(buildNode_ instanceof Function || args.length === 0);
let id = t.identifier(this.preludeGenerator.nameGenerator.generate("derived"));
let value = buildValue(id.name);
let id = this.preludeGenerator.nameGenerator.generate("derived");
let value = buildValue(id);
value.intrinsicNameGenerated = true;
value._isScopedTemplate = true; // because this object doesn't exist ahead of time, and the visitor would otherwise declare it in the common scope
this._addDerivedEntry(id.name, {
// Operation descriptors are immutable so we need create a new version to update properties
let derivedOperationDescriptor = createOperationDescriptor(
operationDescriptor.type,
Object.assign({}, operationDescriptor.data, { id }),
"DERIVED"
);
this._addDerivedEntry(id, {
isPure: optionalArgs ? optionalArgs.isPure : undefined,
declared: value,
args,
buildNode: (nodes: Array<BabelNodeExpression>, context: SerializationContext, valuesToProcess) => {
return t.variableDeclaration("var", [
t.variableDeclarator(
id,
(buildNode_: any) instanceof Function
? ((buildNode_: any): DerivedExpressionBuildNodeFunction)(nodes, context, valuesToProcess)
: ((buildNode_: any): BabelNodeExpression)
),
]);
},
operationDescriptor: derivedOperationDescriptor,
});
return value;
}
@ -1032,18 +1044,16 @@ export class Generator {
types: TypesDomain,
values: ValuesDomain,
args: Array<Value>,
buildNode_: DerivedExpressionBuildNodeFunction | BabelNodeExpression,
operationDescriptor: OperationDescriptor,
optionalArgs?: {|
kind?: AbstractValueKind,
isPure?: boolean,
skipInvariant?: boolean,
mutatesOnly?: Array<Value>,
temporalType?: TemporalBuildNodeType,
shape?: void | ShapeInformationInterface,
|}
): AbstractValue {
invariant(buildNode_ instanceof Function || args.length === 0);
let id = t.identifier(this.preludeGenerator.nameGenerator.generate("derived"));
let id = this.preludeGenerator.nameGenerator.generate("derived");
let options = {};
if (optionalArgs && optionalArgs.kind !== undefined) options.kind = optionalArgs.kind;
if (optionalArgs && optionalArgs.shape !== undefined) options.shape = optionalArgs.shape;
@ -1054,28 +1064,24 @@ export class Generator {
values,
1735003607742176 + this.realm.derivedIds.size,
[],
id,
createOperationDescriptor("IDENTIFIER", { id }),
options
);
this._addDerivedEntry(id.name, {
// Operation descriptor are immutable so we need create a new version to update properties
let derivedOperationDescriptor = createOperationDescriptor(
operationDescriptor.type,
Object.assign({}, operationDescriptor.data, { id }),
"DERIVED"
);
this._addDerivedEntry(id, {
isPure: optionalArgs ? optionalArgs.isPure : undefined,
declared: res,
args,
buildNode: (nodes: Array<BabelNodeExpression>, context: SerializationContext, valuesToProcess) => {
return t.variableDeclaration("var", [
t.variableDeclarator(
id,
(buildNode_: any) instanceof Function
? ((buildNode_: any): DerivedExpressionBuildNodeFunction)(nodes, context, valuesToProcess)
: ((buildNode_: any): BabelNodeExpression)
),
]);
},
operationDescriptor: derivedOperationDescriptor,
mutatesOnly: optionalArgs ? optionalArgs.mutatesOnly : undefined,
temporalType: optionalArgs ? optionalArgs.temporalType : undefined,
});
let type = types.getType();
res.intrinsicName = id.name;
res.intrinsicName = id;
if (optionalArgs && optionalArgs.skipInvariant) return res;
let typeofString;
if (type instanceof FunctionValue) typeofString = "function";
@ -1092,24 +1098,8 @@ export class Generator {
// should mean the model is wrong.
this._emitInvariant(
[res, res],
nodes => {
invariant(typeofString !== undefined);
let condition = t.binaryExpression(
"!==",
t.unaryExpression("typeof", nodes[0]),
t.stringLiteral(typeofString)
);
if (typeofString === "object") {
condition = t.logicalExpression(
"&&",
condition,
t.binaryExpression("!==", t.unaryExpression("typeof", nodes[0]), t.stringLiteral("function"))
);
condition = t.logicalExpression("||", condition, t.binaryExpression("===", nodes[0], nullExpression));
}
return condition;
},
node => node
createOperationDescriptor("DERIVED_ABSTRACT_INVARIANT", { typeofString }),
createOperationDescriptor("SINGLE_ARG")
);
}
@ -1153,7 +1143,8 @@ export class Generator {
_addEntry(entryArgs: TemporalBuildNodeEntryArgs): TemporalBuildNodeEntry {
let entry;
if (entryArgs.temporalType === "OBJECT_ASSIGN") {
let operationDescriptor = entryArgs.operationDescriptor;
if (operationDescriptor && operationDescriptor.type === "OBJECT_ASSIGN") {
entry = new TemporalObjectAssignEntry(this.realm, entryArgs);
} else {
entry = new TemporalBuildNodeEntry(this.realm, entryArgs);
@ -1179,20 +1170,10 @@ export class Generator {
} else {
this._addEntry({
args: [],
buildNode: function(args, context, valuesToProcess) {
let statements = context.serializeGenerator(other, valuesToProcess);
if (statements.length === 1) {
let statement = statements[0];
if (leadingComment.length > 0)
statement.leadingComments = [({ type: "BlockComment", value: leadingComment }: any)];
return statement;
}
let block = t.blockStatement(statements);
if (leadingComment.length > 0)
block.leadingComments = [({ type: "BlockComment", value: leadingComment }: any)];
return block;
},
dependencies: [other],
operationDescriptor: createOperationDescriptor("APPEND_GENERATOR", {
generator: other,
propName: leadingComment,
}),
});
}
}
@ -1200,16 +1181,11 @@ export class Generator {
joinGenerators(joinCondition: AbstractValue, generator1: Generator, generator2: Generator): void {
invariant(generator1 !== this && generator2 !== this && generator1 !== generator2);
if (generator1.empty() && generator2.empty()) return;
let generators = [generator1, generator2];
this._addEntry({
args: [joinCondition],
buildNode: function([cond], context, valuesToProcess) {
let block1 = generator1.empty() ? null : serializeBody(generator1, context, valuesToProcess);
let block2 = generator2.empty() ? null : serializeBody(generator2, context, valuesToProcess);
if (block1) return t.ifStatement(cond, block1, block2);
invariant(block2);
return t.ifStatement(t.unaryExpression("!", cond), block2);
},
dependencies: [generator1, generator2],
operationDescriptor: createOperationDescriptor("JOIN_GENERATORS", { generators }),
dependencies: generators,
});
}
}

View File

@ -39,6 +39,7 @@ import type {
import invariant from "../invariant.js";
import { Logger } from "./logger.js";
import { SerializerStatistics } from "../serializer/statistics.js";
import { createOperationDescriptor } from "./generator.js";
function downgradeErrorsToWarnings(realm: Realm, f: () => any) {
let savedHandler = realm.errorHandler;
@ -243,8 +244,13 @@ export class ModuleTracer extends Tracer {
);
}
result = AbstractValue.createTemporalFromBuildFunction(realm, Value, [], ([]) =>
t.callExpression(t.identifier("require"), [t.valueToNode(moduleIdValue)])
let propName = moduleIdValue + "";
invariant(typeof propName === "string");
result = AbstractValue.createTemporalFromBuildFunction(
realm,
Value,
[],
createOperationDescriptor("MODULES_REQUIRE", { propName })
);
} else {
result = effects.result;

View File

@ -15,7 +15,7 @@ import type { Realm } from "../realm.js";
import { ThrowCompletion } from "../completions.js";
import { StringValue } from "../values/index.js";
import { Construct } from "../methods/construct.js";
import traverseFast from "../utils/traverse-fast.js";
import traverseFast from "./traverse-fast.js";
import { parse } from "@babel/parser";
import type { BabelNodeFile } from "@babel/types";

View File

@ -16,8 +16,8 @@ import invariant from "../invariant.js";
import { Realm } from "../realm.js";
import { AbstractValue, BooleanValue, ConcreteValue, Value } from "../values/index.js";
import { Path, To } from "../singletons.js";
import EmptyValue from "../values/EmptyValue";
import * as t from "@babel/types";
import EmptyValue from "../values/EmptyValue.js";
import { createOperationDescriptor } from "./generator.js";
export default function simplifyAndRefineAbstractValue(
realm: Realm,
@ -128,10 +128,7 @@ function simplify(realm, value: Value, isCondition: boolean = false): Value {
realm,
BooleanValue,
[xa],
([n]) => {
let callFunc = t.identifier("global.__cannotBecomeObject");
return t.callExpression(callFunc, [n]);
},
createOperationDescriptor("CANNOT_BECOME_OBJECT"),
{ kind: "global.__cannotBecomeObject(A)" }
);
}

View File

@ -23,8 +23,6 @@ import {
StringValue,
Value,
} from "./index.js";
import { protoExpression, memberExpressionHelper } from "../utils/babelhelpers.js";
import type { AbstractValueBuildNodeFunction } from "./AbstractValue.js";
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import {
GetFromArrayWithWidenedNumericProperty,
@ -33,9 +31,8 @@ import {
equalDescriptors,
} from "../methods/index.js";
import { Havoc, Widen } from "../singletons.js";
import type { BabelNodeExpression } from "@babel/types";
import invariant from "../invariant.js";
import * as t from "@babel/types";
import { createOperationDescriptor, type OperationDescriptor } from "../utils/generator.js";
export default class AbstractObjectValue extends AbstractValue {
constructor(
@ -44,10 +41,10 @@ export default class AbstractObjectValue extends AbstractValue {
values: ValuesDomain,
hashValue: number,
args: Array<Value>,
buildNode?: AbstractValueBuildNodeFunction | BabelNodeExpression,
operationDescriptor?: OperationDescriptor,
optionalArgs?: {| kind?: AbstractValueKind, intrinsicName?: string, shape?: ShapeInformationInterface |}
) {
super(realm, types, values, hashValue, args, buildNode, optionalArgs);
super(realm, types, values, hashValue, args, operationDescriptor, optionalArgs);
if (!values.isTop()) {
for (let element of this.values.getElements()) invariant(element instanceof ObjectValue);
}
@ -263,13 +260,12 @@ export default class AbstractObjectValue extends AbstractValue {
} else if (this.kind === "explicit conversion to object") {
let primitiveValue = this.args[0];
invariant(!Value.isTypeCompatibleWith(primitiveValue.getType(), PrimitiveValue));
let result = AbstractValue.createFromBuildFunction(realm, ObjectValue, [primitiveValue], ([p]) => {
invariant(realm.preludeGenerator !== undefined);
let getPrototypeOf = realm.preludeGenerator.memoizeReference("Object.getPrototypeOf");
return realm.isCompatibleWith(realm.MOBILE_JSC_VERSION) || realm.isCompatibleWith("mobile")
? t.memberExpression(p, protoExpression)
: t.callExpression(getPrototypeOf, [p]);
});
let result = AbstractValue.createFromBuildFunction(
realm,
ObjectValue,
[primitiveValue],
createOperationDescriptor("ABSTRACT_OBJECT_GET_PROTO_OF")
);
invariant(result instanceof AbstractObjectValue);
return result;
} else {
@ -523,15 +519,7 @@ export default class AbstractObjectValue extends AbstractValue {
this.$Realm,
type,
[ob],
([o]) => {
invariant(typeof P === "string");
return propertyGetter !== undefined
? t.callExpression(t.memberExpression(t.identifier("global"), t.identifier("__prop_" + propertyGetter)), [
o,
t.stringLiteral(P),
])
: memberExpressionHelper(o, P);
},
createOperationDescriptor("ABSTRACT_OBJECT_GET", { propertyGetter, propName: P }),
{
skipInvariant: true,
isPure: true,
@ -635,7 +623,7 @@ export default class AbstractObjectValue extends AbstractValue {
this.$Realm,
Value,
[this, P],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("ABSTRACT_OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
}
@ -655,7 +643,7 @@ export default class AbstractObjectValue extends AbstractValue {
this.$Realm,
Value,
[Receiver, P],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("ABSTRACT_OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
}
@ -782,20 +770,14 @@ export default class AbstractObjectValue extends AbstractValue {
P = P.value;
}
if (typeof P === "string") {
generator.emitStatement([Receiver, V], ([objectNode, valueNode]) => {
invariant(typeof P === "string");
return t.expressionStatement(
t.assignmentExpression("=", memberExpressionHelper(objectNode, P), valueNode)
);
});
generator.emitStatement(
[Receiver, V],
createOperationDescriptor("ABSTRACT_OBJECT_SET_PARTIAL", { propName: P })
);
} else {
// Coercion can only have effects on anything reachable from the key.
Havoc.value(this.$Realm, P);
generator.emitStatement([Receiver, P, V], ([objectNode, keyNode, valueNode]) =>
t.expressionStatement(
t.assignmentExpression("=", memberExpressionHelper(objectNode, keyNode), valueNode)
)
);
generator.emitStatement([Receiver, P, V], createOperationDescriptor("ABSTRACT_OBJECT_SET_PARTIAL_VALUE"));
}
return this.$Realm.intrinsics.undefined;
},

View File

@ -12,14 +12,13 @@
import type {
BabelBinaryOperator,
BabelNodeExpression,
BabelNodeIdentifier,
BabelNodeLogicalOperator,
BabelNodeSourceLocation,
BabelUnaryOperator,
} from "@babel/types";
import { CompilerDiagnostic, FatalError } from "../errors.js";
import type { Realm } from "../realm.js";
import { PreludeGenerator, type TemporalBuildNodeType } from "../utils/generator.js";
import { createOperationDescriptor, PreludeGenerator, type OperationDescriptor } from "../utils/generator.js";
import type { PropertyKeyValue, ShapeInformationInterface } from "../types.js";
import buildExpressionTemplate from "../utils/builder.js";
@ -40,10 +39,6 @@ import { hashString, hashBinary, hashCall, hashTernary, hashUnary } from "../met
import { TypesDomain, ValuesDomain } from "../domains/index.js";
import invariant from "../invariant.js";
import * as t from "@babel/types";
export type AbstractValueBuildNodeFunction = (Array<BabelNodeExpression>) => BabelNodeExpression;
// In addition to the explicitly listed kinds,
// all strings that start with `AbstractValueKindPrefix` are also legal kinds.
export type AbstractValueKind =
@ -100,7 +95,7 @@ export default class AbstractValue extends Value {
values: ValuesDomain,
hashValue: number,
args: Array<Value>,
buildNode?: AbstractValueBuildNodeFunction | BabelNodeExpression,
operationDescriptor?: OperationDescriptor,
optionalArgs?: {| kind?: AbstractValueKind, intrinsicName?: string, shape?: ShapeInformationInterface |}
) {
invariant(realm.useAbstractInterpretation);
@ -110,7 +105,7 @@ export default class AbstractValue extends Value {
this.types = types;
this.values = values;
this.mightBeEmpty = false;
this._buildNode = buildNode;
this.operationDescriptor = operationDescriptor;
this.args = args;
this.hashValue = hashValue;
this.kind = optionalArgs ? optionalArgs.kind : undefined;
@ -124,7 +119,7 @@ export default class AbstractValue extends Value {
mightBeEmpty: boolean;
args: Array<Value>;
shape: void | ShapeInformationInterface;
_buildNode: void | AbstractValueBuildNodeFunction | BabelNodeExpression;
operationDescriptor: void | OperationDescriptor;
toDisplayString(): string {
return "[Abstract " + this.hashValue.toString() + "]";
@ -133,9 +128,10 @@ export default class AbstractValue extends Value {
addSourceLocationsTo(locations: Array<BabelNodeSourceLocation>, seenValues?: Set<AbstractValue> = new Set()): void {
if (seenValues.has(this)) return;
seenValues.add(this);
if (this._buildNode && !(this._buildNode instanceof Function)) {
if (this._buildNode.loc) locations.push(this._buildNode.loc);
}
// TODO: make this work again?
// if (this._buildNode && !(this._buildNode instanceof Function)) {
// if (this._buildNode.loc) locations.push(this._buildNode.loc);
// }
for (let val of this.args) {
if (val instanceof AbstractValue) val.addSourceLocationsTo(locations, seenValues);
}
@ -174,13 +170,6 @@ export default class AbstractValue extends Value {
add_args(this.args);
}
buildNode(args: Array<BabelNodeExpression>): BabelNodeExpression {
let buildNode = this.getBuildNode();
return buildNode instanceof Function
? ((buildNode: any): AbstractValueBuildNodeFunction)(args)
: ((buildNode: any): BabelNodeExpression);
}
equals(x: Value): boolean {
if (x instanceof ConcreteValue) return false;
let thisArgs = this.args;
@ -209,11 +198,6 @@ export default class AbstractValue extends Value {
);
}
getBuildNode(): AbstractValueBuildNodeFunction | BabelNodeExpression {
invariant(this._buildNode);
return this._buildNode;
}
getHash(): number {
return this.hashValue;
}
@ -222,13 +206,16 @@ export default class AbstractValue extends Value {
return this.types.getType();
}
getIdentifier(): BabelNodeIdentifier {
getIdentifier(): string {
invariant(this.hasIdentifier());
return ((this._buildNode: any): BabelNodeIdentifier);
invariant(this.operationDescriptor !== undefined);
let { id } = this.operationDescriptor.data;
invariant(id !== undefined);
return id;
}
hasIdentifier(): boolean {
return this._buildNode ? this._buildNode.type === "Identifier" : false;
return this.operationDescriptor ? this.operationDescriptor.type === "IDENTIFIER" : false;
}
_checkAbstractValueImpliesCounter(): void {
@ -608,9 +595,8 @@ export default class AbstractValue extends Value {
? ValuesDomain.topVal
: ValuesDomain.binaryOp(realm, op, leftValues, rightValues);
let [hash, args] = kind === undefined ? hashBinary(op, left, right) : hashCall(kind, left, right);
let result = new AbstractValue(realm, resultTypes, resultValues, hash, args, ([x, y]) =>
t.binaryExpression(op, x, y)
);
let operationDescriptor = createOperationDescriptor("BINARY_EXPRESSION", { op });
let result = new AbstractValue(realm, resultTypes, resultValues, hash, args, operationDescriptor);
result.kind = kind || op;
result.expressionLocation = loc;
if (doNotSimplify) return result;
@ -654,9 +640,8 @@ export default class AbstractValue extends Value {
let Constructor = Value.isTypeCompatibleWith(resultTypes.getType(), ObjectValue)
? AbstractObjectValue
: AbstractValue;
let result = new Constructor(realm, resultTypes, resultValues, hash, args, ([x, y]) =>
t.logicalExpression(op, x, y)
);
let operationDescriptor = createOperationDescriptor("LOGICAL_EXPRESSION", { op });
let result = new Constructor(realm, resultTypes, resultValues, hash, args, operationDescriptor);
result.kind = op;
result.expressionLocation = loc;
if (doNotSimplify) return result;
@ -686,9 +671,8 @@ export default class AbstractValue extends Value {
let values = ValuesDomain.joinValues(realm, left, right);
let [hash, args] = hashTernary(condition, left || realm.intrinsics.undefined, right || realm.intrinsics.undefined);
let Constructor = Value.isTypeCompatibleWith(types.getType(), ObjectValue) ? AbstractObjectValue : AbstractValue;
let result = new Constructor(realm, types, values, hash, args, ([c, x, y]) => t.conditionalExpression(c, x, y), {
kind: "conditional",
});
let operationDescriptor = createOperationDescriptor("CONDITIONAL_EXPRESSION");
let result = new Constructor(realm, types, values, hash, args, operationDescriptor, { kind: "conditional" });
result.expressionLocation = loc;
if (left) result.mightBeEmpty = left.mightHaveBeenDeleted();
if (right && !result.mightBeEmpty) result.mightBeEmpty = right.mightHaveBeenDeleted();
@ -710,8 +694,14 @@ export default class AbstractValue extends Value {
invariant(op !== "delete" && op !== "++" && op !== "--"); // The operation must be pure
let resultTypes = TypesDomain.unaryOp(op, new TypesDomain(operand.getType()));
let resultValues = ValuesDomain.unaryOp(realm, op, operand.values);
let result = new AbstractValue(realm, resultTypes, resultValues, hashUnary(op, operand), [operand], ([x]) =>
t.unaryExpression(op, x, prefix)
let operationDescriptor = createOperationDescriptor("UNARY_EXPRESSION", { op, prefix });
let result = new AbstractValue(
realm,
resultTypes,
resultValues,
hashUnary(op, operand),
[operand],
operationDescriptor
);
result.kind = op;
result.expressionLocation = loc;
@ -741,13 +731,8 @@ export default class AbstractValue extends Value {
let Constructor = Value.isTypeCompatibleWith(resultType, ObjectValue) ? AbstractObjectValue : AbstractValue;
let labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
invariant(labels.length >= operands.length);
let result = new Constructor(realm, resultTypes, resultValues, hash, operands, args => {
invariant(realm.preludeGenerator !== undefined);
let generatorArgs = {};
let i = 0;
for (let arg of args) generatorArgs[labels.charAt(i++)] = arg;
return template(realm.preludeGenerator)(generatorArgs);
});
let operationDescriptor = createOperationDescriptor("ABSTRACT_FROM_TEMPLATE", { template });
let result = new Constructor(realm, resultTypes, resultValues, hash, operands, operationDescriptor);
result.kind = kind;
result.expressionLocation = loc || realm.currentLocation;
return result;
@ -785,7 +770,6 @@ export default class AbstractValue extends Value {
isPure?: boolean,
skipInvariant?: boolean,
mutatesOnly?: Array<Value>,
temporalType?: TemporalBuildNodeType,
shape?: ShapeInformationInterface,
|}
): AbstractValue {
@ -794,16 +778,16 @@ export default class AbstractValue extends Value {
let types = temp.types;
let values = temp.values;
let args = temp.args;
let buildNode_ = temp.getBuildNode();
invariant(realm.generator !== undefined);
return realm.generator.deriveAbstract(types, values, args, buildNode_, optionalArgs);
invariant(temp.operationDescriptor !== undefined);
return realm.generator.deriveAbstract(types, values, args, temp.operationDescriptor, optionalArgs);
}
static createFromBuildFunction(
realm: Realm,
resultType: typeof Value,
args: Array<Value>,
buildFunction: AbstractValueBuildNodeFunction,
operationDescriptor: OperationDescriptor,
optionalArgs?: {| kind?: AbstractValueKind |}
): AbstractValue | UndefinedValue {
let types = new TypesDomain(resultType);
@ -812,7 +796,7 @@ export default class AbstractValue extends Value {
let kind = (optionalArgs && optionalArgs.kind) || "build function";
let hash;
[hash, args] = hashCall(kind, ...args);
let result = new Constructor(realm, types, values, hash, args, buildFunction);
let result = new Constructor(realm, types, values, hash, args, operationDescriptor);
result.kind = kind;
return result;
}
@ -821,13 +805,12 @@ export default class AbstractValue extends Value {
realm: Realm,
resultType: typeof Value,
args: Array<Value>,
buildFunction: AbstractValueBuildNodeFunction,
operationDescriptor: OperationDescriptor,
optionalArgs?: {|
kind?: AbstractValueKind,
isPure?: boolean,
skipInvariant?: boolean,
mutatesOnly?: Array<Value>,
temporalType?: TemporalBuildNodeType,
shape?: void | ShapeInformationInterface,
|}
): AbstractValue | UndefinedValue {
@ -835,14 +818,14 @@ export default class AbstractValue extends Value {
let values = ValuesDomain.topVal;
invariant(realm.generator !== undefined);
if (resultType === UndefinedValue) {
return realm.generator.emitVoidExpression(types, values, args, buildFunction);
return realm.generator.emitVoidExpression(types, values, args, operationDescriptor);
} else {
return realm.generator.deriveAbstract(types, values, args, buildFunction, optionalArgs);
return realm.generator.deriveAbstract(types, values, args, operationDescriptor, optionalArgs);
}
}
// Creates a union of an abstract value with one or more concrete values.
// The build node for the abstract values becomes the build node for the union.
// The operation descriptor for the abstract values becomes the operation descriptor for the union.
// Use this only to allow instrinsic abstract objects to be null and/or undefined.
static createAbstractConcreteUnion(realm: Realm, ...elements: Array<Value>): AbstractValue {
let concreteValues: Array<ConcreteValue> = (elements.filter(e => e instanceof ConcreteValue): any);
@ -859,7 +842,7 @@ export default class AbstractValue extends Value {
}
let types = TypesDomain.topVal;
let [hash, operands] = hashCall("abstractConcreteUnion", abstractValue, ...concreteValues);
let result = new AbstractValue(realm, types, values, hash, operands, nodes => nodes[0], {
let result = new AbstractValue(realm, types, values, hash, operands, createOperationDescriptor("SINGLE_ARG"), {
kind: "abstractConcreteUnion",
});
result.expressionLocation = realm.currentLocation;
@ -870,13 +853,13 @@ export default class AbstractValue extends Value {
realm: Realm,
resultTemplate: AbstractValue,
args: Array<Value>,
buildFunction: AbstractValueBuildNodeFunction
operationDescriptor: OperationDescriptor
): AbstractValue {
let types = resultTemplate.types;
let values = resultTemplate.values;
let [hash] = hashCall("widened property", ...args);
let Constructor = Value.isTypeCompatibleWith(types.getType(), ObjectValue) ? AbstractObjectValue : AbstractValue;
let result = new Constructor(realm, types, values, hash, args, buildFunction);
let result = new Constructor(realm, types, values, hash, args, operationDescriptor);
result.kind = "widened property";
result.mightBeEmpty = resultTemplate.mightBeEmpty;
result.expressionLocation = resultTemplate.expressionLocation;
@ -909,11 +892,11 @@ export default class AbstractValue extends Value {
let realmPreludeGenerator = realm.preludeGenerator;
invariant(realmPreludeGenerator);
let id = t.identifier(name);
let types = new TypesDomain(type);
let values = ValuesDomain.topVal;
let Constructor = Value.isTypeCompatibleWith(type, ObjectValue) ? AbstractObjectValue : AbstractValue;
let result = new Constructor(realm, types, values, 943586754858 + hashString(name), [], id);
let operationDescriptor = createOperationDescriptor("IDENTIFIER", { id: name });
let result = new Constructor(realm, types, values, 943586754858 + hashString(name), [], operationDescriptor);
result.kind = AbstractValue.makeKind("abstractCounted", (realm.objectCount++).toString()); // need not be an object, but must be unique
result.expressionLocation = location;
result.shape = shape;
@ -999,14 +982,8 @@ export default class AbstractValue extends Value {
realm,
ObjectValue,
temporalArgs,
([targetNode, ...sourceNodes]: Array<BabelNodeExpression>) => {
return t.callExpression(preludeGenerator.memoizeReference("Object.assign"), [targetNode, ...sourceNodes]);
},
{
skipInvariant: true,
mutatesOnly: [to],
temporalType: "OBJECT_ASSIGN",
}
createOperationDescriptor("OBJECT_ASSIGN"),
{ skipInvariant: true, mutatesOnly: [to] }
);
invariant(temporalTo instanceof AbstractObjectValue);
if (to instanceof AbstractObjectValue) {

View File

@ -11,13 +11,13 @@
import type { Realm } from "../realm.js";
import type { PropertyKeyValue, Descriptor } from "../types.js";
import { ObjectValue, Value } from "../values/index.js";
import { ObjectValue, Value } from "./index.js";
import { IsDataDescriptor, IsAccessorDescriptor } from "../methods/is.js";
import { HasOwnProperty } from "../methods/has.js";
import { SameValuePartial } from "../methods/abstract.js";
import { Get, OrdinaryGet } from "../methods/get.js";
import { Properties } from "../singletons.js";
import invariant from "../invariant";
import invariant from "../invariant.js";
export default class ArgumentsExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {

View File

@ -14,8 +14,8 @@ import type { PropertyKeyValue, Descriptor, ObjectKind } from "../types.js";
import { AbstractValue, ObjectValue, StringValue, NumberValue, Value } from "./index.js";
import { IsAccessorDescriptor, IsPropertyKey, IsArrayIndex } from "../methods/is.js";
import { Properties, To } from "../singletons.js";
import { type OperationDescriptor } from "../utils/generator.js";
import invariant from "../invariant.js";
import type { BabelNodeExpression } from "@babel/types";
export default class ArrayValue extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {
@ -97,7 +97,7 @@ export default class ArrayValue extends ObjectValue {
static createTemporalWithWidenedNumericProperty(
realm: Realm,
args: Array<Value>,
buildFunction: (Array<BabelNodeExpression>) => BabelNodeExpression,
operationDescriptor: OperationDescriptor,
reactArrayHint?: { func: Value, thisVal: Value }
): ArrayValue {
invariant(realm.generator !== undefined);
@ -105,7 +105,7 @@ export default class ArrayValue extends ObjectValue {
let value = realm.generator.deriveConcreteObject(
intrinsicName => new ArrayValue(realm, intrinsicName),
args,
buildFunction,
operationDescriptor,
{ isPure: true }
);

View File

@ -14,7 +14,7 @@ import type { BabelNodeBlockStatement, BabelNodeSourceLocation, BabelNodeLVal }
import type { FunctionBodyAstNode } from "../types.js";
import { ECMAScriptFunctionValue } from "./index.js";
import * as t from "@babel/types";
import invariant from "../invariant";
import invariant from "../invariant.js";
/* Non built-in ECMAScript function objects with source code */
export default class ECMAScriptSourceFunctionValue extends ECMAScriptFunctionValue {

View File

@ -11,13 +11,13 @@
import type { Realm } from "../realm.js";
import type { PropertyKeyValue, Descriptor } from "../types.js";
import { ObjectValue, NumberValue, StringValue, Value, UndefinedValue } from "../values/index.js";
import { ObjectValue, NumberValue, StringValue, Value, UndefinedValue } from "./index.js";
import { IsInteger, IsArrayIndex, IsAccessorDescriptor, IsDetachedBuffer, IsPropertyKey } from "../methods/is.js";
import { OrdinaryGet } from "../methods/get.js";
import { OrdinaryHasProperty } from "../methods/has.js";
import { IntegerIndexedElementSet, IntegerIndexedElementGet } from "../methods/typedarray.js";
import { Properties, To } from "../singletons.js";
import invariant from "../invariant";
import invariant from "../invariant.js";
export default class IntegerIndexedExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {

View File

@ -54,8 +54,7 @@ import {
import { Havoc, Properties, To } from "../singletons.js";
import invariant from "../invariant.js";
import type { typeAnnotation } from "@babel/types";
import * as t from "@babel/types";
import { memberExpressionHelper } from "../utils/babelhelpers.js";
import { createOperationDescriptor } from "../utils/generator.js";
function isWidenedValue(v: void | Value) {
if (!(v instanceof AbstractValue)) return false;
@ -572,10 +571,14 @@ export default class ObjectValue extends ConcreteValue {
if (realm.react.enabled && realm.react.reactProps.has(this)) {
realm.react.reactProps.add(template);
}
let result = AbstractValue.createTemporalFromBuildFunction(this.$Realm, ObjectValue, [template], ([x]) => x, {
skipInvariant: true,
isPure: true,
});
let operationDescriptor = createOperationDescriptor("SINGLE_ARG");
let result = AbstractValue.createTemporalFromBuildFunction(
this.$Realm,
ObjectValue,
[template],
operationDescriptor,
{ skipInvariant: true, isPure: true }
);
invariant(result instanceof AbstractObjectValue);
result.values = new ValuesDomain(template);
return result;
@ -770,7 +773,7 @@ export default class ObjectValue extends ConcreteValue {
this.$Realm,
Value,
[Receiver, P],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
} else {
@ -798,7 +801,7 @@ export default class ObjectValue extends ConcreteValue {
this.$Realm,
Value,
[this, P],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
}
@ -808,7 +811,7 @@ export default class ObjectValue extends ConcreteValue {
this.$Realm,
Value,
[this, P],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
}
@ -861,7 +864,7 @@ export default class ObjectValue extends ConcreteValue {
this.$Realm,
absVal.getType(),
[ob, propName],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
}
@ -879,7 +882,7 @@ export default class ObjectValue extends ConcreteValue {
this.$Realm,
absVal.getType(),
[ob, propName],
([o, p]) => memberExpressionHelper(o, p),
createOperationDescriptor("OBJECT_GET_PARTIAL"),
{ skipInvariant: true, isPure: true }
);
} else if (arg2.args.length === 3) {
@ -941,9 +944,7 @@ export default class ObjectValue extends ConcreteValue {
let generator = this.$Realm.generator;
invariant(generator);
invariant(P instanceof AbstractValue);
generator.emitStatement([Receiver, P, V], ([objectNode, keyNode, valueNode]) =>
t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, keyNode), valueNode))
);
generator.emitStatement([Receiver, P, V], createOperationDescriptor("OBJECT_SET_PARTIAL"));
return this.$Realm.intrinsics.undefined;
},
TypesDomain.topVal,

View File

@ -11,10 +11,10 @@
import type { Realm } from "../realm.js";
import type { PropertyKeyValue, Descriptor } from "../types.js";
import { ObjectValue, NumberValue, StringValue } from "../values/index.js";
import { ObjectValue, NumberValue, StringValue } from "./index.js";
import { IsInteger, IsArrayIndex } from "../methods/is.js";
import { Properties, To } from "../singletons.js";
import invariant from "../invariant";
import invariant from "../invariant.js";
export default class StringExotic extends ObjectValue {
constructor(realm: Realm, intrinsicName?: string) {

View File

@ -10,7 +10,7 @@
/* @flow strict-local */
import { hashString } from "../methods/index.js";
import { PrimitiveValue, Value } from "../values/index.js";
import { PrimitiveValue, Value } from "./index.js";
import type { Realm } from "../realm.js";
export default class StringValue extends PrimitiveValue {

View File

@ -37,6 +37,6 @@ export { default as BooleanValue } from "./BooleanValue.js";
export { default as StringValue } from "./StringValue.js";
export { default as SymbolValue } from "./SymbolValue.js";
export { default as AbstractValue, AbstractValueBuildNodeFunction } from "./AbstractValue.js";
export { default as AbstractValue } from "./AbstractValue.js";
export type { AbstractValueKind } from "./AbstractValue.js";
export { default as AbstractObjectValue } from "./AbstractObjectValue.js";