UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

260 lines (207 loc) 9.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSuggestedArrayLiteralLength = getSuggestedArrayLiteralLength; exports.commonAncestorOf = commonAncestorOf; exports.getOrDefault = getOrDefault; exports.withDescriptorValue = withDescriptorValue; exports.canIgnoreClassLengthProperty = canIgnoreClassLengthProperty; exports.getObjectPrototypeMetadata = getObjectPrototypeMetadata; exports.createAdditionalEffects = createAdditionalEffects; exports.handleReportedSideEffect = handleReportedSideEffect; exports.ClassPropertiesToIgnore = void 0; var _index = require("../values/index.js"); var _errors = require("../errors.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _index2 = require("../methods/index.js"); var _logger = require("../utils/logger.js"); var _generator = require("../utils/generator.js"); var _babelhelpers = require("../utils/babelhelpers.js"); var _descriptors = require("../descriptors.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. */ /** * Get index property list length by searching array properties list for the max index key value plus 1. * If tail elements are conditional, return the minimum length if an assignment to the length property * can be avoided because of that. The boolean part of the result is a flag that indicates if the latter is true. */ function getSuggestedArrayLiteralLength(realm, val) { (0, _invariant.default)((0, _index2.IsArray)(realm, val)); let instantRenderMode = realm.instantRender.enabled; let minLength = 0, maxLength = 0; let actualLength; for (const key of val.properties.keys()) { if ((0, _index2.IsArrayIndex)(realm, key) && Number(key) >= maxLength) { let prevMax = maxLength; maxLength = Number(key) + 1; let elem = val._SafeGetDataPropertyValue(key); if (instantRenderMode || !elem.mightHaveBeenDeleted()) minLength = maxLength;else if (elem instanceof _index.AbstractValue && elem.kind === "conditional") { let maxLengthVal = new _index.IntegralValue(realm, maxLength); let [c, x, y] = elem.args; if (x instanceof _index.EmptyValue && !y.mightHaveBeenDeleted()) { let prevActual = actualLength === undefined ? new _index.IntegralValue(realm, prevMax) : actualLength; actualLength = _index.AbstractValue.createFromConditionalOp(realm, c, prevActual, maxLengthVal); } else if (y instanceof _index.EmptyValue && !x.mightHaveBeenDeleted()) { let prevActual = actualLength === undefined ? new _index.IntegralValue(realm, prevMax) : actualLength; actualLength = _index.AbstractValue.createFromConditionalOp(realm, c, maxLengthVal, prevActual); } else { actualLength = undefined; } } } } if (maxLength > minLength && actualLength instanceof _index.AbstractValue) { let lengthVal = val._SafeGetDataPropertyValue("length"); if (lengthVal.equals(actualLength)) return [minLength, true]; } return [maxLength, false]; } function commonAncestorOf(node1, node2, getParent) { if (node1 === node2) return node1; // First get the path length to the root node for both nodes while also checking if // either node is the parent of the other. let n1 = node1, n2 = node2, count1 = 0, count2 = 0; while (true) { let p1 = n1 && getParent(n1); let p2 = n2 && getParent(n2); if (p1 === node2) return node2; if (p2 === node1) return node1; if (p1 !== undefined) count1++; if (p2 !== undefined) count2++; if (p1 === undefined && p2 === undefined) break; n1 = p1; n2 = p2; } // Now shorten the longest path to the same length as the shorter path n1 = node1; while (count1 > count2) { (0, _invariant.default)(n1 !== undefined); n1 = getParent(n1); count1--; } n2 = node2; while (count1 < count2) { (0, _invariant.default)(n2 !== undefined); n2 = getParent(n2); count2--; } // Now run up both paths in tandem, stopping at the first common entry while (n1 !== n2) { (0, _invariant.default)(n1 !== undefined); n1 = getParent(n1); (0, _invariant.default)(n2 !== undefined); n2 = getParent(n2); } return n1; } // Gets map[key] with default value provided by defaultFn function getOrDefault(map, key, defaultFn) { let value = map.get(key); if (value === undefined) map.set(key, value = defaultFn()); (0, _invariant.default)(value !== undefined); return value; } function withDescriptorValue(propertyNameOrSymbol, descriptor, func) { if (descriptor !== undefined) { (0, _invariant.default)(descriptor instanceof _descriptors.PropertyDescriptor); // TODO: Handle joined descriptors. if (descriptor.value !== undefined) { func(propertyNameOrSymbol, descriptor.value, "value"); } else { if (descriptor.get !== undefined) { func(propertyNameOrSymbol, descriptor.get, "get"); } if (descriptor.set !== undefined) { func(propertyNameOrSymbol, descriptor.set, "set"); } } } } const ClassPropertiesToIgnore = new Set(["arguments", "name", "caller"]); exports.ClassPropertiesToIgnore = ClassPropertiesToIgnore; function canIgnoreClassLengthProperty(val, desc, logger) { if (desc) { if (desc instanceof _descriptors.PropertyDescriptor) { if (desc.value === undefined) { logger.logError(val, "Functions with length accessor properties are not supported in residual heap."); } } else { logger.logError(val, "Functions with length properties with different attributes are not supported in residual heap."); } } return true; } function getObjectPrototypeMetadata(realm, obj) { let proto = obj.$Prototype; let skipPrototype = false; let constructor; if (obj.$IsClassPrototype) { skipPrototype = true; } if (proto && proto.$IsClassPrototype) { (0, _invariant.default)(proto instanceof _index.ObjectValue); // we now need to check if the prototpe has a constructor let _constructor = proto.properties.get("constructor"); if (_constructor !== undefined) { // if the contructor has been deleted then we have no way // to serialize the original class AST as it won't have been // evluated and thus visited if (_constructor.descriptor === undefined) { throw new _errors.FatalError("TODO #1024: implement object prototype serialization with deleted constructor"); } if (!(_constructor.descriptor instanceof _descriptors.PropertyDescriptor)) { throw new _errors.FatalError("TODO #1024: implement object prototype serialization with multiple constructor attributes"); } let classFunc = _constructor.descriptor.value; if (classFunc instanceof _index.ECMAScriptSourceFunctionValue) { constructor = classFunc; skipPrototype = true; } } } return { skipPrototype, constructor }; } function createAdditionalEffects(realm, effects, fatalOnAbrupt, name, additionalFunctionEffects, preEvaluationComponentToWriteEffectFunction, optimizedFunction, parentOptimizedFunction, transforms = []) { let generator = _generator.Generator.fromEffects(effects, realm, name, additionalFunctionEffects, preEvaluationComponentToWriteEffectFunction, optimizedFunction); let retValue = { parentAdditionalFunction: parentOptimizedFunction || undefined, effects, transforms, generator, additionalRoots: new Set() }; return retValue; } function handleReportedSideEffect(exceptionHandler, sideEffectType, binding, expressionLocation) { // This causes an infinite recursion because creating a callstack causes internal-only side effects if (binding && binding.object && binding.object.intrinsicName === "__checkedBindings") return; let location = (0, _babelhelpers.optionalStringOfLocation)(expressionLocation); if (sideEffectType === "MODIFIED_BINDING") { let name = binding ? `"${binding.name}"` : "unknown"; exceptionHandler(`side-effects from mutating the binding ${name}${location}`, expressionLocation); } else if (sideEffectType === "MODIFIED_PROPERTY" || sideEffectType === "MODIFIED_GLOBAL") { let name = ""; let pb = binding; let key = pb.key; if (typeof key === "string") { name = `"${key}"`; } if (sideEffectType === "MODIFIED_PROPERTY") { if (!_index.ObjectValue.refuseSerializationOnPropertyBinding(pb)) exceptionHandler(`side-effects from mutating a property ${name}${location}`, expressionLocation); } else { exceptionHandler(`side-effects from mutating the global object property ${name}${location}`, expressionLocation); } } else if (sideEffectType === "EXCEPTION_THROWN") { exceptionHandler(`side-effects from throwing exception${location}`, expressionLocation); } } //# sourceMappingURL=utils.js.map