prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
316 lines (234 loc) • 12.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = require("./index.js");
var _is = require("../methods/is.js");
var _singletons = require("../singletons.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _errors = require("../errors.js");
var _descriptors = require("../descriptors.js");
var _completions = require("../completions.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* 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.
*/
/* strict-local */
function evaluatePossibleNestedOptimizedFunctionsAndStoreEffects(realm, abstractArrayValue, possibleNestedOptimizedFunctions) {
for (let _ref of possibleNestedOptimizedFunctions) {
let {
func,
thisValue
} = _ref;
let funcToModel = func;
if (func instanceof _index.BoundFunctionValue) {
funcToModel = func.$BoundTargetFunction;
thisValue = func.$BoundThis;
}
(0, _invariant.default)(funcToModel instanceof _index.ECMAScriptSourceFunctionValue);
if (funcToModel.isCalledInMultipleContexts) return;
let previouslyComputedEffects = realm.collectedNestedOptimizedFunctionEffects.get(funcToModel);
if (previouslyComputedEffects !== undefined) {
if (realm.instantRender.enabled) {
realm.instantRenderBailout("Array operators may only be optimized once", funcToModel.expressionLocation);
} else {
// We currently do not support context-sensitive specialization,
// where the calls we specialize depend on the specialization context.
// TODO: #2454
// TODO: Implement context-sensitive specialization instead of giving up
funcToModel.isCalledInMultipleContexts = true;
_singletons.Leak.value(realm, func);
return;
}
}
let funcCall = _singletons.Utils.createModelledFunctionCall(realm, funcToModel, undefined, thisValue); // We take the modelled function and wrap it in a pure evaluation so we can check for
// side-effects that occur when evaluating the function. If there are side-effects, then
// we don't try and optimize the nested function.
let pureFuncCall = () => realm.evaluatePure(funcCall,
/*bubbles*/
false, () => {
throw new _errors.NestedOptimizedFunctionSideEffect();
});
let effects;
try {
effects = realm.evaluateForEffects(pureFuncCall, null, "temporalArray nestedOptimizedFunction");
} catch (e) {
// If the nested optimized function had side-effects, we need to fallback to
// the default behaviour and leaked the nested functions so any bindings
// within the function properly leak and materialize.
if (e instanceof _errors.NestedOptimizedFunctionSideEffect) {
if (realm.instantRender.enabled) {
realm.instantRenderBailout("InstantRender does not support impure array operators", funcCall.expressionLocation);
}
_singletons.Leak.value(realm, func);
return;
}
throw e;
} // Check if effects were pure then add them
if (abstractArrayValue.nestedOptimizedFunctionEffects === undefined) {
abstractArrayValue.nestedOptimizedFunctionEffects = new Map();
}
abstractArrayValue.nestedOptimizedFunctionEffects.set(funcToModel, effects);
realm.collectedNestedOptimizedFunctionEffects.set(funcToModel, effects);
}
}
/*
We track aliases explicitly, because we currently do not have the primitives to model objects created
inside of the loop. TODO: Revisit when #2543 and subsequent modeling work
lands. At that point, instead of of a mayAliasSet, we can return a widened
abstract value.
*/
function modelUnknownPropertyOfSpecializedArray(realm, args, array, possibleNestedOptimizedFunctions) {
let sentinelProperty = {
key: undefined,
descriptor: new _descriptors.PropertyDescriptor({
writable: true,
enumerable: true,
configurable: true
}),
object: array
};
let mayAliasedObjects = new Set();
if (realm.arrayNestedOptimizedFunctionsEnabled && possibleNestedOptimizedFunctions) {
(0, _invariant.default)(possibleNestedOptimizedFunctions.length > 0);
if (possibleNestedOptimizedFunctions[0].kind === "map") {
for (let _ref2 of possibleNestedOptimizedFunctions) {
let {
func
} = _ref2;
let funcToModel;
if (func instanceof _index.BoundFunctionValue) {
funcToModel = func.$BoundTargetFunction;
} else {
funcToModel = func;
}
(0, _invariant.default)(funcToModel instanceof _index.ECMAScriptSourceFunctionValue);
if (array.nestedOptimizedFunctionEffects !== undefined) {
let effects = array.nestedOptimizedFunctionEffects.get(funcToModel);
if (effects !== undefined) {
(0, _invariant.default)(effects.result instanceof _completions.SimpleNormalCompletion);
let reachableObjects = _singletons.Materialize.computeReachableObjects(realm, effects.result.value);
for (let reachableObject of reachableObjects) {
if (!effects.createdObjects.has(reachableObject)) mayAliasedObjects.add(reachableObject);
}
}
}
}
} // For filter, we just collect the may alias set of the mapped array
if (args.length > 0) {
let mappedArray = args[0];
if (ArrayValue.isIntrinsicAndHasWidenedNumericProperty(mappedArray)) {
(0, _invariant.default)(mappedArray instanceof ArrayValue);
(0, _invariant.default)(mappedArray.unknownProperty !== undefined);
(0, _invariant.default)(mappedArray.unknownProperty.descriptor instanceof _descriptors.PropertyDescriptor);
let unknownPropertyValue = mappedArray.unknownProperty.descriptor.value;
(0, _invariant.default)(unknownPropertyValue instanceof _index.AbstractValue);
let aliasSet = unknownPropertyValue.args[0];
(0, _invariant.default)(aliasSet instanceof _index.AbstractValue && aliasSet.kind === "mayAliasSet");
for (let aliasedObject of aliasSet.args) {
(0, _invariant.default)(aliasedObject instanceof _index.ObjectValue);
mayAliasedObjects.add(aliasedObject);
}
}
}
}
let aliasSet = _index.AbstractValue.createFromType(realm, _index.Value, "mayAliasSet", [...mayAliasedObjects]);
sentinelProperty.descriptor.value = _index.AbstractValue.createFromType(realm, _index.Value, "widened numeric property", [aliasSet]);
return sentinelProperty;
}
function createArrayWithWidenedNumericProperty(realm, args, intrinsicName, possibleNestedOptimizedFunctions) {
let abstractArrayValue = new ArrayValue(realm, intrinsicName);
if (possibleNestedOptimizedFunctions !== undefined && possibleNestedOptimizedFunctions.length > 0) {
if (realm.arrayNestedOptimizedFunctionsEnabled && (!realm.react.enabled || realm.react.optimizeNestedFunctions)) {
evaluatePossibleNestedOptimizedFunctionsAndStoreEffects(realm, abstractArrayValue, possibleNestedOptimizedFunctions);
} else {
// If nested optimized functions are disabled, we need to fallback to
// the default behaviour and leaked the nested functions so any bindings
// within the function properly leak and materialize.
for (let _ref3 of possibleNestedOptimizedFunctions) {
let {
func
} = _ref3;
_singletons.Leak.value(realm, func);
}
}
} // Add unknownProperty so we manually handle this object property access
abstractArrayValue.unknownProperty = modelUnknownPropertyOfSpecializedArray(realm, args, abstractArrayValue, possibleNestedOptimizedFunctions);
return abstractArrayValue;
}
class ArrayValue extends _index.ObjectValue {
constructor(realm, intrinsicName) {
super(realm, realm.intrinsics.ArrayPrototype, intrinsicName);
}
getKind() {
return "Array";
}
isSimpleObject() {
return this.$TypedArrayName === undefined;
} // ECMA262 9.4.2.1
$DefineOwnProperty(P, Desc) {
let A = this; // 1. Assert: IsPropertyKey(P) is true.
(0, _invariant.default)((0, _is.IsPropertyKey)(this.$Realm, P), "expected a property key"); // 2. If P is "length", then
if (P === "length" || P instanceof _index.StringValue && P.value === "length") {
// a. Return ? ArraySetLength(A, Desc).
return _singletons.Properties.ArraySetLength(this.$Realm, A, Desc);
} else if ((0, _is.IsArrayIndex)(this.$Realm, P)) {
if (ArrayValue.isIntrinsicAndHasWidenedNumericProperty(this)) {
// The length of an array with widenend numeric properties is always abstract
let succeeded = _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, A, P, Desc);
if (succeeded === false) return false;
return true;
} // 3. Else if P is an array index, then
// a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
let oldLenDesc = _singletons.Properties.OrdinaryGetOwnProperty(this.$Realm, A, "length"); // b. Assert: oldLenDesc will never be undefined or an accessor descriptor because Array objects are
// created with a length data property that cannot be deleted or reconfigured.
(0, _invariant.default)(oldLenDesc !== undefined && !(0, _is.IsAccessorDescriptor)(this.$Realm, oldLenDesc), "cannot be undefined or an accessor descriptor");
_singletons.Properties.ThrowIfMightHaveBeenDeleted(oldLenDesc);
oldLenDesc = oldLenDesc.throwIfNotConcrete(this.$Realm); // c. Let oldLen be oldLenDesc.[[Value]].
let oldLen = oldLenDesc.value;
(0, _invariant.default)(oldLen instanceof _index.Value);
oldLen = oldLen.throwIfNotConcrete();
(0, _invariant.default)(oldLen instanceof _index.NumberValue, "expected number value");
oldLen = oldLen.value; // d. Let index be ! ToUint32(P).
let index = _singletons.To.ToUint32(this.$Realm, typeof P === "string" ? new _index.StringValue(this.$Realm, P) : P); // e. If index ≥ oldLen and oldLenDesc.[[Writable]] is false, return false.
if (index >= oldLen && oldLenDesc.writable === false) return false; // f. Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc).
let succeeded = _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, A, P, Desc); // g. If succeeded is false, return false.
if (succeeded === false) return false; // h. If index ≥ oldLen, then
if (index >= oldLen) {
// i. Set oldLenDesc.[[Value]] to index + 1.
oldLenDesc.value = new _index.NumberValue(this.$Realm, index + 1); // ii. Let succeeded be OrdinaryDefineOwnProperty(A, "length", oldLenDesc).
succeeded = _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, A, "length", oldLenDesc); // iii. Assert: succeeded is true.
(0, _invariant.default)(succeeded, "expected length definition to succeed");
} // i. Return true.
return true;
} // 1. Return OrdinaryDefineOwnProperty(A, P, Desc).
return _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, A, P, Desc);
}
static createTemporalWithWidenedNumericProperty(realm, args, operationDescriptor, possibleNestedOptimizedFunctions) {
(0, _invariant.default)(realm.generator !== undefined);
let value = realm.generator.deriveConcreteObject(intrinsicName => createArrayWithWidenedNumericProperty(realm, args, intrinsicName, possibleNestedOptimizedFunctions), args, operationDescriptor, {
isPure: true
});
(0, _invariant.default)(value instanceof ArrayValue);
return value;
}
static isIntrinsicAndHasWidenedNumericProperty(obj) {
if (obj instanceof ArrayValue && obj.intrinsicName !== undefined && obj.isScopedTemplate !== undefined) {
(0, _invariant.default)(_index.ObjectValue.isIntrinsicDerivedObject(obj));
const prop = obj.unknownProperty;
if (prop !== undefined && prop.descriptor !== undefined) {
const desc = prop.descriptor.throwIfNotConcrete(obj.$Realm);
return desc.value instanceof _index.AbstractValue && desc.value.kind === "widened numeric property";
}
}
return false;
}
}
exports.default = ArrayValue;
//# sourceMappingURL=ArrayValue.js.map