UNPKG

prepack

Version:

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

203 lines (158 loc) 7.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.canHoistFunction = canHoistFunction; exports.canHoistReactElement = canHoistReactElement; exports.determineIfReactElementCanBeHoisted = determineIfReactElementCanBeHoisted; var _realm = require("../realm.js"); var _index = require("../values/index.js"); var _index2 = require("../methods/index.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _utils = require("./utils.js"); var _ResidualHeapVisitor = require("../serializer/ResidualHeapVisitor.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. */ // a nested object of a React Element should be hoisted where all its properties are known // at evaluation time to be safe to hoist (because of the heuristics of a React render) function canHoistObject(realm, object, residualHeapVisitor, visitedValues) { if ((0, _utils.isReactElement)(object)) { return canHoistReactElement(realm, object, residualHeapVisitor, visitedValues); } for (let [propName] of object.properties) { let prop = (0, _index2.Get)(realm, object, propName); if (!canHoistValue(realm, prop, residualHeapVisitor, visitedValues)) { return false; } } for (let [symbol] of object.symbols) { let prop = (0, _index2.Get)(realm, object, symbol); if (!canHoistValue(realm, prop, residualHeapVisitor, visitedValues)) { return false; } } return true; } function canHoistArray(realm, array, residualHeapVisitor, visitedValues) { if (array.intrinsicName) return false; let lengthValue = (0, _index2.Get)(realm, array, "length"); if (!canHoistValue(realm, lengthValue, residualHeapVisitor, visitedValues)) { return false; } if (lengthValue instanceof _index.NumberValue) { let length = lengthValue.value; for (let i = 0; i < length; i++) { let element = (0, _index2.Get)(realm, array, "" + i); if (!canHoistValue(realm, element, residualHeapVisitor, visitedValues)) { return false; } } } return true; } function canHoistFunction(realm, func, residualHeapVisitor, visitedValues) { if (realm.react.hoistableFunctions.has(func)) { // cast because Flow thinks that we may have set a value to be something other than a boolean? return realm.react.hoistableFunctions.get(func); } if (residualHeapVisitor === undefined) { return false; } // get the function instance let functionInstance = residualHeapVisitor.functionInstances.get(func); // we can safely hoist the function if the residual bindings hoistable too if (functionInstance !== undefined) { (0, _invariant.default)(functionInstance.residualFunctionBindings instanceof Map); let residualBindings = functionInstance.residualFunctionBindings; for (let [, { declarativeEnvironmentRecord, value }] of residualBindings) { // if declarativeEnvironmentRecord is null, it's likely a global binding // so we can assume that we can still hoist this function if (declarativeEnvironmentRecord !== null) { if (value === undefined) { return false; } (0, _invariant.default)(value instanceof _index.Value); if (!canHoistValue(realm, value, residualHeapVisitor, visitedValues)) { return false; } } } if (func instanceof _index.ECMAScriptSourceFunctionValue) { let code = func.$ECMAScriptCode; let functionInfos = residualHeapVisitor.functionInfos.get(code); if (functionInfos && functionInfos.unbound.size > 0) { return false; } } realm.react.hoistableFunctions.set(func, true); return true; } realm.react.hoistableFunctions.set(func, false); return false; } function canHoistAbstract(realm, abstract, residualHeapVisitor) { // TODO #1687: add abstract value hoisting return false; } function isPrimitive(realm, value) { return value instanceof _index.StringValue || value instanceof _index.NumberValue || value instanceof _index.SymbolValue || value instanceof _index.BooleanValue || value === realm.intrinsics.null || value === realm.intrinsics.undefined; } function canHoistValue(realm, value, residualHeapVisitor, visitedValues) { if (visitedValues.has(value)) { // If there is a cycle, bail out. // TODO: is there some way to *not* bail out in this case? // Currently if we don't, the output is broken. return false; } visitedValues.add(value); let canHoist = false; if (value instanceof _index.ArrayValue) { canHoist = canHoistArray(realm, value, residualHeapVisitor, visitedValues); } else if (value instanceof _index.FunctionValue) { canHoist = canHoistFunction(realm, value, residualHeapVisitor, visitedValues); } else if (value instanceof _index.ObjectValue) { canHoist = canHoistObject(realm, value, residualHeapVisitor, visitedValues); } else if (value instanceof _index.AbstractValue) { canHoist = canHoistAbstract(realm, value, residualHeapVisitor); } else if (isPrimitive) { canHoist = true; } visitedValues.delete(value); return canHoist; } function canHoistReactElement(realm, reactElement, residualHeapVisitor, visitedValues) { if (realm.react.hoistableReactElements.has(reactElement)) { // cast because Flow thinks that we may have set a value to be something other than a boolean? return realm.react.hoistableReactElements.get(reactElement); } if (residualHeapVisitor === undefined) { return false; } let type = (0, _utils.getProperty)(realm, reactElement, "type"); let ref = (0, _utils.getProperty)(realm, reactElement, "ref"); let key = (0, _utils.getProperty)(realm, reactElement, "key"); let props = (0, _utils.getProperty)(realm, reactElement, "props"); if (visitedValues === undefined) { visitedValues = new Set(); visitedValues.add(reactElement); } if (canHoistValue(realm, type, residualHeapVisitor, visitedValues) && // we can't hoist string "refs" or if they're abstract, as they might be abstract strings !(ref instanceof String || ref instanceof _index.AbstractValue) && canHoistValue(realm, ref, residualHeapVisitor, visitedValues) && canHoistValue(realm, key, residualHeapVisitor, visitedValues) && !props.isPartialObject() && canHoistValue(realm, props, residualHeapVisitor, visitedValues)) { realm.react.hoistableReactElements.set(reactElement, true); return true; } realm.react.hoistableReactElements.set(reactElement, false); return false; } function determineIfReactElementCanBeHoisted(realm, reactElement, residualHeapVisitor) { canHoistReactElement(realm, reactElement, residualHeapVisitor); } //# sourceMappingURL=hoisting.js.map