diff --git a/src/environment.js b/src/environment.js index 99f73c52b..7b00e9b29 100644 --- a/src/environment.js +++ b/src/environment.js @@ -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 { diff --git a/src/evaluators/BinaryExpression.js b/src/evaluators/BinaryExpression.js index 19b337bd5..93558d987 100644 --- a/src/evaluators/BinaryExpression.js +++ b/src/evaluators/BinaryExpression.js @@ -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) => t.binaryExpression(op, lnode, rnode) + createOperationDescriptor("BINARY_EXPRESSION", { op }) ), TypesDomain.topVal, ValuesDomain.topVal diff --git a/src/evaluators/CallExpression.js b/src/evaluators/CallExpression.js index c30ce9ebe..6f8376167 100644 --- a/src/evaluators/CallExpression.js +++ b/src/evaluators/CallExpression.js @@ -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); - return t.callExpression(callFunc, fun_args); - }); + return AbstractValue.createTemporalFromBuildFunction( + realm, + resultType, + args, + createOperationDescriptor("CALL_BAILOUT", { propRef: propName, thisArg }) + ); } function tryToEvaluateCallOrLeaveAsAbstract( diff --git a/src/evaluators/ForStatement.js b/src/evaluators/ForStatement.js index f1b4a8e52..70fc3888e 100644 --- a/src/evaluators/ForStatement.js +++ b/src/evaluators/ForStatement.js @@ -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; diff --git a/src/evaluators/NewExpression.js b/src/evaluators/NewExpression.js index 32a30de7b..e2b2776b8 100644 --- a/src/evaluators/NewExpression.js +++ b/src/evaluators/NewExpression.js @@ -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) => - t.newExpression(constructorNode, argListNodes) + createOperationDescriptor("NEW_EXPRESSION") ), TypesDomain.topVal, ValuesDomain.topVal diff --git a/src/evaluators/UpdateExpression.js b/src/evaluators/UpdateExpression.js index 4401afaf8..319ce8a20 100644 --- a/src/evaluators/UpdateExpression.js +++ b/src/evaluators/UpdateExpression.js @@ -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 diff --git a/src/intrinsics/ecma262/Array.js b/src/intrinsics/ecma262/Array.js index fa65c1a84..4ca1a407b 100644 --- a/src/intrinsics/ecma262/Array.js +++ b/src/intrinsics/ecma262/Array.js @@ -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)), + createOperationDescriptor("UNKNOWN_ARRAY_METHOD_CALL"), { func: mapfn, thisVal: thisArg } ); } diff --git a/src/intrinsics/ecma262/ArrayProto_toString.js b/src/intrinsics/ecma262/ArrayProto_toString.js index 9fef26e0d..2568ec6b2 100644 --- a/src/intrinsics/ecma262/ArrayProto_toString.js +++ b/src/intrinsics/ecma262/ArrayProto_toString.js @@ -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" }) ); } diff --git a/src/intrinsics/ecma262/ArrayProto_values.js b/src/intrinsics/ecma262/ArrayProto_values.js index f02ae70a4..2f4e3a88c 100644 --- a/src/intrinsics/ecma262/ArrayProto_values.js +++ b/src/intrinsics/ecma262/ArrayProto_values.js @@ -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" }) ); } diff --git a/src/intrinsics/ecma262/ArrayPrototype.js b/src/intrinsics/ecma262/ArrayPrototype.js index 64b5e538f..f1b561ad5 100644 --- a/src/intrinsics/ecma262/ArrayPrototype.js +++ b/src/intrinsics/ecma262/ArrayPrototype.js @@ -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)) + 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)) + 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)) + 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)) + 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)), + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)), + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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)) + 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" }) ); } diff --git a/src/intrinsics/ecma262/Object.js b/src/intrinsics/ecma262/Object.js index d451b3c56..2185b8097 100644 --- a/src/intrinsics/ecma262/Object.js +++ b/src/intrinsics/ecma262/Object.js @@ -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") ); } diff --git a/src/intrinsics/ecma262/ObjectPrototype.js b/src/intrinsics/ecma262/ObjectPrototype.js index 4053f66a6..bd6e06423 100644 --- a/src/intrinsics/ecma262/ObjectPrototype.js +++ b/src/intrinsics/ecma262/ObjectPrototype.js @@ -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) => { - return t.callExpression(t.memberExpression(methodNode, t.identifier("call")), [objectNode, nameNode]); - } + createOperationDescriptor("OBJECT_PROTO_HAS_OWN_PROPERTY") ), TypesDomain.topVal, ValuesDomain.topVal diff --git a/src/intrinsics/fb-www/fb-mocks.js b/src/intrinsics/fb-www/fb-mocks.js index 2a98ad4f3..40b0de602 100644 --- a/src/intrinsics/fb-www/fb-mocks.js +++ b/src/intrinsics/fb-www/fb-mocks.js @@ -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)), + 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) - ), + createOperationDescriptor("FB_MOCKS_BOOTLOADER_LOAD_MODULES"), { skipInvariant: true } ); invariant(val instanceof AbstractValue); diff --git a/src/intrinsics/fb-www/react-dom-mocks.js b/src/intrinsics/fb-www/react-dom-mocks.js index 847439a91..6481af4f3 100644 --- a/src/intrinsics/fb-www/react-dom-mocks.js +++ b/src/intrinsics/fb-www/react-dom-mocks.js @@ -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)); - }, + 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)); - }, + 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)); - }, + createOperationDescriptor("REACT_TEMPORAL_FUNC"), { skipInvariant: true, isPure: true } ); invariant(reactDomMethod instanceof AbstractObjectValue); diff --git a/src/intrinsics/fb-www/react-mocks.js b/src/intrinsics/fb-www/react-mocks.js index 7226982f1..5547da700 100644 --- a/src/intrinsics/fb-www/react-mocks.js +++ b/src/intrinsics/fb-www/react-mocks.js @@ -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); diff --git a/src/intrinsics/fb-www/react-native-mocks.js b/src/intrinsics/fb-www/react-native-mocks.js index a764aa2da..3d92c248e 100644 --- a/src/intrinsics/fb-www/react-native-mocks.js +++ b/src/intrinsics/fb-www/react-native-mocks.js @@ -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); diff --git a/src/intrinsics/fb-www/relay-mocks.js b/src/intrinsics/fb-www/relay-mocks.js index b7ff6fb29..a35883446 100644 --- a/src/intrinsics/fb-www/relay-mocks.js +++ b/src/intrinsics/fb-www/relay-mocks.js @@ -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) - ); - }, + createOperationDescriptor("REACT_RELAY_MOCK_CONTAINER", { propName: containerName }), { skipInvariant: true, isPure: true } ); invariant(value instanceof AbstractValue); diff --git a/src/intrinsics/prepack/global.js b/src/intrinsics/prepack/global.js index a5efd96b6..7abc8c23e 100644 --- a/src/intrinsics/prepack/global.js +++ b/src/intrinsics/prepack/global.js @@ -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): 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)) + 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) => void, - buildNode_: (Array) => 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) => { - 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, diff --git a/src/intrinsics/prepack/utils.js b/src/intrinsics/prepack/utils.js index 932bc2411..27cbeae67 100644 --- a/src/intrinsics/prepack/utils.js +++ b/src/intrinsics/prepack/utils.js @@ -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);})()"; diff --git a/src/methods/arraybuffer.js b/src/methods/arraybuffer.js index fcec61b5d..bd480799a 100644 --- a/src/methods/arraybuffer.js +++ b/src/methods/arraybuffer.js @@ -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"; diff --git a/src/methods/call.js b/src/methods/call.js index 3ad5e9a1f..714aedc7b 100644 --- a/src/methods/call.js +++ b/src/methods/call.js @@ -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) => { - let fun_args = nodes.slice(1); - return t.callExpression(nodes[0], ((fun_args: any): Array)); - } + createOperationDescriptor("DIRECT_CALL_WITH_ARG_LIST") ); } func = func.throwIfNotConcrete(); @@ -594,16 +591,20 @@ export function Call(realm: Realm, F: Value, V: Value, argsList?: Array): } 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); - 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); - 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); diff --git a/src/methods/environment.js b/src/methods/environment.js index 609d5d00e..df5f7b683 100644 --- a/src/methods/environment.js +++ b/src/methods/environment.js @@ -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) diff --git a/src/methods/function.js b/src/methods/function.js index 8279b9e74..d34c8b72c 100644 --- a/src/methods/function.js +++ b/src/methods/function.js @@ -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, diff --git a/src/methods/get.js b/src/methods/get.js index c8c838787..240d3d1f1 100644 --- a/src/methods/get.js +++ b/src/methods/get.js @@ -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, diff --git a/src/methods/json.js b/src/methods/json.js index 12bb97592..77dd393b9 100644 --- a/src/methods/json.js +++ b/src/methods/json.js @@ -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"; diff --git a/src/methods/promise.js b/src/methods/promise.js index 2442cdff5..edb2b4940 100644 --- a/src/methods/promise.js +++ b/src/methods/promise.js @@ -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"; diff --git a/src/methods/properties.js b/src/methods/properties.js index 82ab0e97f..377a7c47e 100644 --- a/src/methods/properties.js +++ b/src/methods/properties.js @@ -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 diff --git a/src/methods/to.js b/src/methods/to.js index da6cbb4b7..09c05e256 100644 --- a/src/methods/to.js +++ b/src/methods/to.js @@ -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. diff --git a/src/methods/typedarray.js b/src/methods/typedarray.js index 3109e27c1..e59dee2fd 100644 --- a/src/methods/typedarray.js +++ b/src/methods/typedarray.js @@ -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"; diff --git a/src/methods/widen.js b/src/methods/widen.js index e78cb3ade..29a259369 100644 --- a/src/methods/widen.js +++ b/src/methods/widen.js @@ -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") ); } } diff --git a/src/react/ReactEquivalenceSet.js b/src/react/ReactEquivalenceSet.js index 61382bf60..ac0962a23 100644 --- a/src/react/ReactEquivalenceSet.js +++ b/src/react/ReactEquivalenceSet.js @@ -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; diff --git a/src/react/branching.js b/src/react/branching.js index 5d201c898..ba6448970 100644 --- a/src/react/branching.js +++ b/src/react/branching.js @@ -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); diff --git a/src/react/components.js b/src/react/components.js index 84c5dae7f..6b572d7ab 100644 --- a/src/react/components.js +++ b/src/react/components.js @@ -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"; diff --git a/src/react/elements.js b/src/react/elements.js index 764813e6f..b290b35dd 100644 --- a/src/react/elements.js +++ b/src/react/elements.js @@ -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)); - }, + createOperationDescriptor("REACT_DEFAULT_PROPS_HELPER"), { skipInvariant: true } ); invariant(temporalTo instanceof AbstractObjectValue); diff --git a/src/react/experimental-server-rendering/rendering.js b/src/react/experimental-server-rendering/rendering.js index d90d7b537..afe35fdc4 100644 --- a/src/react/experimental-server-rendering/rendering.js +++ b/src/react/experimental-server-rendering/rendering.js @@ -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 | 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), 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); diff --git a/src/react/jsx.js b/src/react/jsx.js index 0f4b4fde9..cecc80c9b 100644 --- a/src/react/jsx.js +++ b/src/react/jsx.js @@ -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, diff --git a/src/react/reconcilation.js b/src/react/reconcilation.js index 8c3020aef..47172bb48 100644 --- a/src/react/reconcilation.js +++ b/src/react/reconcilation.js @@ -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)); - }, + createOperationDescriptor("REACT_TEMPORAL_FUNC"), { skipInvariant: true, isPure: true } ); } diff --git a/src/realm.js b/src/realm.js index 695ee2293..41c79b8f7 100644 --- a/src/realm.js +++ b/src/realm.js @@ -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 = 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); } } diff --git a/src/serializer/Emitter.js b/src/serializer/Emitter.js index 52a742e9b..294c8a433 100644 --- a/src/serializer/Emitter.js +++ b/src/serializer/Emitter.js @@ -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) diff --git a/src/serializer/ResidualFunctionInstantiator.js b/src/serializer/ResidualFunctionInstantiator.js index 46e3802d8..a1729f330 100644 --- a/src/serializer/ResidualFunctionInstantiator.js +++ b/src/serializer/ResidualFunctionInstantiator.js @@ -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) { diff --git a/src/serializer/ResidualHeapSerializer.js b/src/serializer/ResidualHeapSerializer.js index b28bd0002..37040a883 100644 --- a/src/serializer/ResidualHeapSerializer.js +++ b/src/serializer/ResidualHeapSerializer.js @@ -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; mainBody: SerializedBody; realm: Realm; + residualOperationSerializer: ResidualOperationSerializer; preludeGenerator: PreludeGenerator; generator: Generator; _descriptors: Map; @@ -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, + _context: SerializationContext, + valuesToProcess: Set + ) => { + 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 ): Array => - 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"); diff --git a/src/serializer/ResidualOperationSerializer.js b/src/serializer/ResidualOperationSerializer.js new file mode 100644 index 000000000..077302387 --- /dev/null +++ b/src/serializer/ResidualOperationSerializer.js @@ -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 +): 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, + context?: SerializationContext, + valuesToProcess?: Set + ): 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, + context?: SerializationContext, + valuesToProcess?: Set + ) { + 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) { + 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) { + return t.assignmentExpression("=", memberExpressionHelper(o, p), v); + } + + _serializeWidenPropertyAssignment({ propName }: OperationDescriptorData, [o, v]: Array) { + invariant(typeof propName === "string"); + return t.assignmentExpression("=", memberExpressionHelper(o, propName), v); + } + + _serializeWidenAbstractProperty(data: OperationDescriptorData, [o, p]: Array) { + return memberExpressionHelper(o, p); + } + + _serializeWidenProperty({ propName }: OperationDescriptorData, [o]: Array) { + invariant(typeof propName === "string"); + return memberExpressionHelper(o, propName); + } + + _serializeAbstractObjectGet( + { propertyGetter, propName: P }: OperationDescriptorData, + [o]: Array + ) { + 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) { + 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) { + let callFunc = t.identifier("global.__cannotBecomeObject"); + return t.callExpression(callFunc, [n]); + } + + _serializeResidualCall(data: OperationDescriptorData, nodes: Array) { + return t.callExpression(nodes[0], ((nodes.slice(1): any): Array)); + } + + _serializeModulesRequires({ propName }: OperationDescriptorData) { + invariant(propName !== undefined); + return t.callExpression(t.identifier("require"), [t.valueToNode(propName)]); + } + + _serializeConcreteModel({ propName }: OperationDescriptorData, [valueNode]: Array) { + invariant(propName !== undefined); + return t.expressionStatement( + t.assignmentExpression("=", this.preludeGenerator.globalReference(propName, false), valueNode) + ); + } + + _serializeConsoleLog({ propName }: OperationDescriptorData, nodes: Array) { + invariant(propName !== undefined); + return t.expressionStatement( + t.callExpression(t.memberExpression(t.identifier("console"), t.identifier(propName)), [...nodes]) + ); + } + + _serializeDoWhile( + { generator, value }: OperationDescriptorData, + nodes: Array, + context?: SerializationContext, + valuesToProcess?: Set + ) { + 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) { + 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) { + invariant(propName !== undefined); + return t.binaryExpression("!==", memberExpressionHelper(objectNode, propName), valueNode); + } + + _serializeFullInvariantFunction({ propName }: OperationDescriptorData, [objectNode]: Array) { + invariant(typeof propName === "string"); + return t.binaryExpression( + "!==", + t.unaryExpression("typeof", memberExpressionHelper(objectNode, propName), true), + t.stringLiteral("function") + ); + } + + _serializeFullInvariantAbstract( + { concreteComparisons, typeComparisons }: OperationDescriptorData, + [valueNode]: Array + ) { + 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) { + invariant(typeof propName === "string"); + return memberExpressionHelper(objectNode, propName); + } + + _serializePropertyInvariant({ propName, state }: OperationDescriptorData, [objectNode]: Array) { + 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) { + invariant(op !== undefined); + return t.binaryExpression(op, oldValNode, t.numericLiteral(1)); + } + + _serializeDerivedAbstractInvariant({ typeofString }: OperationDescriptorData, nodes: Array) { + 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 + ) { + 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 + ) { + invariant(typeof propName === "string"); + return t.callExpression( + t.memberExpression(reactRelayIdent, t.identifier(propName)), + ((otherArgs: any): Array) + ); + } + + _serializePropertyAssignment( + { path }: OperationDescriptorData, + [o, p, v, e]: Array, + context?: SerializationContext, + valuesToProcess?: Set + ) { + 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, + context?: SerializationContext, + valuesToProcess?: Set + ) { + 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 + ) { + 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, + context?: SerializationContext, + valuesToProcess?: Set + ) { + 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) { + return t.memberExpression(consumerNode, t.identifier("Provider")); + } + + _serializeReactTemporalFunc(data: OperationDescriptorData, [renderNode, ..._args]: Array) { + return t.callExpression(renderNode, ((_args: any): Array)); + } + + _serializeCallAbstractFunc(data: OperationDescriptorData, nodes: Array) { + let fun_args = ((nodes.slice(1): any): Array); + return t.callExpression(nodes[0], fun_args); + } + + _serializeCallAbstractFuncThis(data: OperationDescriptorData, nodes: Array) { + let fun_args = ((nodes.slice(1): any): Array); + return t.callExpression(t.memberExpression(nodes[0], t.identifier("call")), fun_args); + } + + _serializeDirectCallWithArgList(data: OperationDescriptorData, nodes: Array) { + let fun_args = nodes.slice(1); + return t.callExpression(nodes[0], ((fun_args: any): Array)); + } + + _serializeObjectProtoHasOwnProperty( + data: OperationDescriptorData, + [methodNode, objectNode, nameNode]: Array + ) { + return t.callExpression(t.memberExpression(methodNode, t.identifier("call")), [objectNode, nameNode]); + } + + _serializeRebuiltObject({ propName }: OperationDescriptorData, [node]: Array) { + 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, + 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) { + invariant(typeof propName === "string"); + return t.callExpression(t.identifier(propName), ((args: any): Array)); + } + + _serializeFBMocksBootloaderLoadModules(data: OperationDescriptorData, args: Array) { + return t.callExpression( + t.memberExpression(t.identifier("Bootloader"), t.identifier("loadModules")), + ((args: any): Array) + ); + } + + _serializeAbstractObjectSetPartial( + { propName }: OperationDescriptorData, + [objectNode, valueNode]: Array + ) { + invariant(typeof propName === "string"); + return t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, propName), valueNode)); + } + + _serializeAbstractObjectSetPartialValue( + data: OperationDescriptorData, + [objectNode, keyNode, valueNode]: Array + ) { + return t.expressionStatement(t.assignmentExpression("=", memberExpressionHelper(objectNode, keyNode), valueNode)); + } + + _serializeUnknownArrayGetPartial(data: OperationDescriptorData, [o, p]: Array) { + return memberExpressionHelper(o, p); + } + + _serializeObjectGetPartial(data: OperationDescriptorData, [o, p]: Array) { + return memberExpressionHelper(o, p); + } + + _serializeAbstractObjectGetPartial(data: OperationDescriptorData, [o, p]: Array) { + return memberExpressionHelper(o, p); + } + + _serializeObjectSetPartial( + data: OperationDescriptorData, + [objectNode, keyNode, valueNode]: Array + ) { + 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) { + return t.binaryExpression("+", t.stringLiteral(""), p); + } + + _serializeBabelHelpersObjectWithoutProperties( + data: OperationDescriptorData, + [methodNode, objNode, propRemoveNode]: Array + ) { + return t.callExpression(methodNode, [objNode, propRemoveNode]); + } + + _serializeReactDefaultPropsHelper(data: OperationDescriptorData, [methodNode, ..._args]: Array) { + return t.callExpression(methodNode, ((_args: any): Array)); + } + + _serializeUnknownArrayMethodCall(data: OperationDescriptorData, [methodNode, ..._args]: Array) { + return t.callExpression(methodNode, ((_args: any): Array)); + } + + _serializeUnknownArrayLength(data: OperationDescriptorData, [o]: Array) { + return t.memberExpression(o, t.identifier("length"), false); + } + + _serializeUnknownArrayMethodPropertyCall( + { propName }: OperationDescriptorData, + [objNode, ..._args]: Array + ) { + invariant(typeof propName === "string"); + return t.callExpression(t.memberExpression(objNode, t.identifier(propName)), ((_args: any): Array)); + } + + _serializeThrow(data: OperationDescriptorData, [argument]: Array) { + return t.throwStatement(argument); + } + + _serializeConditionalThrow( + { value }: OperationDescriptorData, + nodes: Array, + 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) { + invariant(quasis !== undefined); + return t.templateLiteral(((quasis: any): Array), valueNodes); + } + + _serializeReactRenderValueHelper(data: OperationDescriptorData, [helperNode, valueNode]: Array) { + return t.callExpression(helperNode, [valueNode]); + } + + _serializePropertyDelete({ propName }: OperationDescriptorData, [objectNode]: Array) { + invariant(propName !== undefined); + return t.expressionStatement(t.unaryExpression("delete", memberExpressionHelper(objectNode, propName))); + } + + _serializeGetBinding( + { binding }: OperationDescriptorData, + nodes: Array, + context?: SerializationContext + ) { + invariant(binding !== undefined); + invariant(context !== undefined); + return context.serializeBinding(((binding: any): Binding)); + } + + _serializeForFunctionCall(data: OperationDescriptorData, [func, thisExpr]: Array) { + let { usesThis } = data; + return usesThis + ? t.callExpression(t.memberExpression(func, t.identifier("call")), [thisExpr]) + : t.callExpression(func, []); + } + + _serializeNewExpression( + data: OperationDescriptorData, + [constructorNode, ...argListNodes]: Array + ) { + return t.newExpression(constructorNode, argListNodes); + } + + _serializeEmitCall({ callTemplate }: OperationDescriptorData, nodes: Array) { + invariant(typeof callTemplate === "function"); + return t.expressionStatement(t.callExpression(callTemplate(), [...nodes])); + } + + _serializeEmitCallAndCaptureResults({ callTemplate }: OperationDescriptorData, nodes: Array) { + invariant(typeof callTemplate === "function"); + return t.callExpression(callTemplate(), ((nodes: any): Array)); + } + + _serializeObjectProtoGetOwnPropertyDescriptor( + data: OperationDescriptorData, + [funcNode, ...args]: Array + ) { + return t.callExpression(funcNode, ((args: any): Array)); + } + + _serializeCallBailout({ propRef, thisArg }: OperationDescriptorData, nodes: Array) { + 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); + return t.callExpression(callFunc, fun_args); + } + + _serializeJoinGenerators( + { generators }: OperationDescriptorData, + [cond]: Array, + context?: SerializationContext, + valuesToProcess?: Set + ): 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, + 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) { + invariant(typeof propName === "string"); + return t.expressionStatement( + t.assignmentExpression("=", this.preludeGenerator.globalReference(propName, false), valueNode) + ); + } + + _serializeSingleArg(data: OperationDescriptorData, [o]: Array) { + return o; + } + + _serializeAbstractProperty({ propName }: OperationDescriptorData, [o]: Array) { + invariant(propName !== undefined); + return memberExpressionHelper(o, propName); + } + + _serializeUnaryExpression({ op, prefix }: OperationDescriptorData, [x, y]: Array) { + invariant(op !== undefined); + return t.unaryExpression(op, x, prefix); + } + + _serializeBinaryExpression({ op }: OperationDescriptorData, [x, y]: Array) { + invariant(op !== undefined); + return t.binaryExpression(op, x, y); + } + + _serializeLogicalExpression({ op }: OperationDescriptorData, [x, y]: Array) { + invariant(op !== undefined); + return t.logicalExpression(op, x, y); + } + + _serializeConditionalExpression(data: OperationDescriptorData, [c, x, y]: Array) { + 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) { + 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) { + return t.callExpression(this.preludeGenerator.memoizeReference("Object.assign"), [targetNode, ...sourceNodes]); + } +} diff --git a/src/serializer/ResidualReactElementSerializer.js b/src/serializer/ResidualReactElementSerializer.js index 2a4892e94..964f25ebc 100644 --- a/src/serializer/ResidualReactElementSerializer.js +++ b/src/serializer/ResidualReactElementSerializer.js @@ -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"; diff --git a/src/serializer/utils.js b/src/serializer/utils.js index 9aca6a173..46cc46d06 100644 --- a/src/serializer/utils.js +++ b/src/serializer/utils.js @@ -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. diff --git a/src/types.js b/src/types.js index f42614278..c202dcf43 100644 --- a/src/types.js +++ b/src/types.js @@ -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, }; diff --git a/src/utils/HeapInspector.js b/src/utils/HeapInspector.js index 8811a18d5..b0ad75268 100644 --- a/src/utils/HeapInspector.js +++ b/src/utils/HeapInspector.js @@ -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" | ""; diff --git a/src/utils/generator.js b/src/utils/generator.js index 4349e742f..068036642 100644 --- a/src/utils/generator.js +++ b/src/utils/generator.js @@ -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, + SerializationContext, + Set +) => 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, + desc?: Descriptor, + generator?: Generator, + generators?: Array, + 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, + state?: "MISSING" | "PRESENT" | "DEFINED", + thisArg?: BaseValue | Value, + template?: PreludeGenerator => ({}) => BabelNodeExpression, + typeComparisons?: Set, + 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, + SerializationContext, + Set + ) => 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, - SerializationContext, - Set -) => BabelNodeExpression; - -export type GeneratorBuildNodeFunction = ( - Array, - SerializationContext, - Set -) => 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, - // 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, isPure?: boolean, mutatesOnly?: Array, - temporalType?: TemporalBuildNodeType, }; export class TemporalBuildNodeEntry extends GeneratorEntry { @@ -163,17 +298,16 @@ export class TemporalBuildNodeEntry extends GeneratorEntry { declared: void | AbstractValue | ObjectValue; args: Array; - // 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; isPure: void | boolean; mutatesOnly: void | Array; - 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 -): 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, 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): void { + emitCall(callTemplate: () => BabelNodeExpression, args: Array): void { this._addEntry({ args, - buildNode: values => t.expressionStatement(t.callExpression(createCallee(), [...values])), + operationDescriptor: createOperationDescriptor("EMIT_CALL", { callTemplate }), }); } emitConsoleLog(method: ConsoleMethodTypes, args: Array): 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, - violationConditionFn: (Array) => 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) => { - 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, 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, buildNode_: (Array) => BabelNodeStatement): void { + emitStatement(args: Array, 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, - buildNode_: AbstractValueBuildNodeFunction | BabelNodeExpression + operationDescriptor: OperationDescriptor ): UndefinedValue { + let voidOperationDescriptor = createOperationDescriptor(operationDescriptor.type, operationDescriptor.data, "VOID"); this._addEntry({ args, - buildNode: (nodes: Array) => - 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, - 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, 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, - buildNode_: DerivedExpressionBuildNodeFunction | BabelNodeExpression, + operationDescriptor: OperationDescriptor, optionalArgs?: {| kind?: AbstractValueKind, isPure?: boolean, skipInvariant?: boolean, mutatesOnly?: Array, - 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, 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, }); } } diff --git a/src/utils/modules.js b/src/utils/modules.js index d75f7b815..ae0b62186 100644 --- a/src/utils/modules.js +++ b/src/utils/modules.js @@ -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; diff --git a/src/utils/parse.js b/src/utils/parse.js index 74d1bd9ff..ff1635d60 100644 --- a/src/utils/parse.js +++ b/src/utils/parse.js @@ -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"; diff --git a/src/utils/simplifier.js b/src/utils/simplifier.js index b415acae9..cf5d3b2e4 100644 --- a/src/utils/simplifier.js +++ b/src/utils/simplifier.js @@ -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)" } ); } diff --git a/src/values/AbstractObjectValue.js b/src/values/AbstractObjectValue.js index 74d4a765b..fc2e7bcf2 100644 --- a/src/values/AbstractObjectValue.js +++ b/src/values/AbstractObjectValue.js @@ -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, - 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; }, diff --git a/src/values/AbstractValue.js b/src/values/AbstractValue.js index f8b25a9e5..a799b5b91 100644 --- a/src/values/AbstractValue.js +++ b/src/values/AbstractValue.js @@ -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; - // 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, - 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; 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, seenValues?: Set = 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 { - 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, - 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, - 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, - buildFunction: AbstractValueBuildNodeFunction, + operationDescriptor: OperationDescriptor, optionalArgs?: {| kind?: AbstractValueKind, isPure?: boolean, skipInvariant?: boolean, mutatesOnly?: Array, - 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): AbstractValue { let concreteValues: Array = (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, - 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) => { - 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) { diff --git a/src/values/ArgumentsExotic.js b/src/values/ArgumentsExotic.js index 3d9eee0e5..49c4b44de 100644 --- a/src/values/ArgumentsExotic.js +++ b/src/values/ArgumentsExotic.js @@ -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) { diff --git a/src/values/ArrayValue.js b/src/values/ArrayValue.js index 06bba9761..e450a01cf 100644 --- a/src/values/ArrayValue.js +++ b/src/values/ArrayValue.js @@ -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, - buildFunction: (Array) => 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 } ); diff --git a/src/values/ECMAScriptSourceFunctionValue.js b/src/values/ECMAScriptSourceFunctionValue.js index b7ccae523..94a99b5ad 100644 --- a/src/values/ECMAScriptSourceFunctionValue.js +++ b/src/values/ECMAScriptSourceFunctionValue.js @@ -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 { diff --git a/src/values/IntegerIndexedExotic.js b/src/values/IntegerIndexedExotic.js index 5bdf19d1f..cb67b2b8c 100644 --- a/src/values/IntegerIndexedExotic.js +++ b/src/values/IntegerIndexedExotic.js @@ -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) { diff --git a/src/values/ObjectValue.js b/src/values/ObjectValue.js index 6ac567983..5790b18d9 100644 --- a/src/values/ObjectValue.js +++ b/src/values/ObjectValue.js @@ -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, diff --git a/src/values/StringExotic.js b/src/values/StringExotic.js index aa1516b05..eedf6921e 100644 --- a/src/values/StringExotic.js +++ b/src/values/StringExotic.js @@ -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) { diff --git a/src/values/StringValue.js b/src/values/StringValue.js index f1a4f3508..cb47a11a8 100644 --- a/src/values/StringValue.js +++ b/src/values/StringValue.js @@ -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 { diff --git a/src/values/index.js b/src/values/index.js index 770374727..61cc2332e 100644 --- a/src/values/index.js +++ b/src/values/index.js @@ -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";