prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
230 lines (182 loc) • 9.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ResidualReactElementVisitor = void 0;
var _realm = require("../realm.js");
var _index = require("../values/index.js");
var _ResidualHeapVisitor = require("./ResidualHeapVisitor.js");
var _hoisting = require("../react/hoisting.js");
var _elements = require("../react/elements.js");
var _utils = require("../react/utils.js");
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _generator = require("../utils/generator.js");
var _ReactEquivalenceSet = require("../react/ReactEquivalenceSet.js");
var _ReactElementSet = require("../react/ReactElementSet.js");
var _ReactPropsSet = require("../react/ReactPropsSet.js");
var _index2 = require("../methods/index.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 */
class ResidualReactElementVisitor {
constructor(realm, residualHeapVisitor) {
this.realm = realm;
this.residualHeapVisitor = residualHeapVisitor;
this.reactOutput = realm.react.output || "create-element";
this.defaultEquivalenceSet = true;
this.reactEquivalenceSet = new _ReactEquivalenceSet.ReactEquivalenceSet(realm, this);
this.reactElementEquivalenceSet = new _ReactElementSet.ReactElementSet(realm, this.reactEquivalenceSet);
this.reactPropsEquivalenceSet = new _ReactPropsSet.ReactPropsSet(realm, this.reactEquivalenceSet);
}
visitReactElement(reactElement) {
let reactElementData = this.realm.react.reactElements.get(reactElement);
(0, _invariant.default)(reactElementData !== undefined);
let {
firstRenderOnly
} = reactElementData;
let isReactFragment = false;
(0, _elements.traverseReactElement)(this.realm, reactElement, {
visitType: typeValue => {
let reactElementStringTypeReferences = this.realm.react.reactElementStringTypeReferences; // If the type is a text value, and we have a derived reference for it
// then use that derived reference instead of the string value. This is
// primarily designed around RCTView and RCTText, which are string values
// for RN apps, but are treated as special host components.
if (typeValue instanceof _index.StringValue && reactElementStringTypeReferences.has(typeValue.value)) {
let reference = reactElementStringTypeReferences.get(typeValue.value);
(0, _invariant.default)(reference instanceof _index.AbstractValue);
(0, _utils.hardModifyReactObjectPropertyBinding)(this.realm, reactElement, "type", reference);
this.residualHeapVisitor.visitValue(reference);
return;
}
isReactFragment = typeValue instanceof _index.SymbolValue && typeValue === (0, _utils.getReactSymbol)("react.fragment", this.realm); // we don't want to visit fragments as they are internal values
if (!isReactFragment) {
this.residualHeapVisitor.visitValue(typeValue);
}
},
visitKey: keyValue => {
this.residualHeapVisitor.visitValue(keyValue);
},
visitRef: refValue => {
if (!firstRenderOnly) {
this.residualHeapVisitor.visitValue(refValue);
}
},
visitAbstractOrPartialProps: propsValue => {
this.residualHeapVisitor.visitValue(propsValue);
},
visitConcreteProps: propsValue => {
for (let [propName, binding] of propsValue.properties) {
(0, _invariant.default)(propName !== "key" && propName !== "ref", `"${propName}" is a reserved prop name`);
if (binding.descriptor === undefined || propName === "children") {
continue;
}
let propValue = (0, _utils.getProperty)(this.realm, propsValue, propName);
if ((0, _utils.canExcludeReactElementObjectProperty)(this.realm, reactElement, propName, propValue)) {
continue;
}
this.residualHeapVisitor.visitValue(propValue);
}
},
visitChildNode: childValue => {
this.residualHeapVisitor.visitValue(childValue);
}
}); // Our serializer requires that every value we serialize must first be visited in every scope where it appears. In
// our React element serializer we serialize some values (namely `React.createElement` and `React.Fragment`) that do
// not necessarily appear in our source code. We must manually visit these values in our visitor pass for the values
// to be serializable.
if (this.realm.react.output === "create-element") {
const reactLibraryObject = this._getReactLibraryValue();
(0, _invariant.default)(reactLibraryObject instanceof _index.ObjectValue);
const createElement = reactLibraryObject.properties.get("createElement");
(0, _invariant.default)(createElement !== undefined);
const reactCreateElement = (0, _index2.Get)(this.realm, reactLibraryObject, "createElement"); // Our `createElement` value will be used in the prelude of the optimized function we serialize to initialize
// our hoisted React elements. So we need to ensure that we visit our value in a scope above our own to allow
// the function to be used in our optimized function prelude. We use our global scope to accomplish this. We are
// a "friend" class of `ResidualHeapVisitor` so we call one of its private methods.
this.residualHeapVisitor._visitInUnrelatedScope(this.residualHeapVisitor.globalGenerator, reactCreateElement);
}
if (isReactFragment) {
const reactLibraryObject = this._getReactLibraryValue(); // Our `React.Fragment` value will be used in the function to lazily initialize hoisted JSX elements. So we need
// to visit the library in our global generator so that it is available when creating the hoisted elements.
this.residualHeapVisitor._visitInUnrelatedScope(this.residualHeapVisitor.globalGenerator, reactLibraryObject);
} // determine if this ReactElement node tree is going to be hoistable
(0, _hoisting.determineIfReactElementCanBeHoisted)(this.realm, reactElement, this.residualHeapVisitor);
}
withCleanEquivalenceSet(func) {
let defaultEquivalenceSet = this.defaultEquivalenceSet;
let reactEquivalenceSet = this.reactEquivalenceSet;
let reactElementEquivalenceSet = this.reactElementEquivalenceSet;
let reactPropsEquivalenceSet = this.reactPropsEquivalenceSet;
this.defaultEquivalenceSet = false;
this.reactEquivalenceSet = new _ReactEquivalenceSet.ReactEquivalenceSet(this.realm, this);
this.reactElementEquivalenceSet = new _ReactElementSet.ReactElementSet(this.realm, this.reactEquivalenceSet);
this.reactPropsEquivalenceSet = new _ReactPropsSet.ReactPropsSet(this.realm, this.reactEquivalenceSet);
func(); // Cleanup
this.defaultEquivalenceSet = defaultEquivalenceSet;
this.reactEquivalenceSet = reactEquivalenceSet;
this.reactElementEquivalenceSet = reactElementEquivalenceSet;
this.reactPropsEquivalenceSet = reactPropsEquivalenceSet;
}
saveEquivalenceSet() {
const {
reactEquivalenceSet,
reactElementEquivalenceSet,
reactPropsEquivalenceSet
} = this;
return {
reactEquivalenceSet,
reactElementEquivalenceSet,
reactPropsEquivalenceSet
};
}
loadEquivalenceSet(save, func) {
const defaultEquivalenceSet = this.defaultEquivalenceSet;
const reactEquivalenceSet = this.reactEquivalenceSet;
const reactElementEquivalenceSet = this.reactElementEquivalenceSet;
const reactPropsEquivalenceSet = this.reactPropsEquivalenceSet;
this.defaultEquivalenceSet = false;
this.reactEquivalenceSet = save.reactEquivalenceSet;
this.reactElementEquivalenceSet = save.reactElementEquivalenceSet;
this.reactPropsEquivalenceSet = save.reactPropsEquivalenceSet;
const result = func(); // Cleanup
this.defaultEquivalenceSet = defaultEquivalenceSet;
this.reactEquivalenceSet = reactEquivalenceSet;
this.reactElementEquivalenceSet = reactElementEquivalenceSet;
this.reactPropsEquivalenceSet = reactPropsEquivalenceSet;
return result;
}
wasTemporalAliasDeclaredInCurrentScope(temporalAlias) {
let scope = this.residualHeapVisitor.scope;
if (scope instanceof _index.FunctionValue) {
return false;
} // If the temporal has already been visited, then we know the temporal
// value was used and thus declared in another scope
if (this.residualHeapVisitor.values.has(temporalAlias)) {
return false;
} // Otherwise, we check the current scope and see if the
// temporal value was declared in one of the entries
for (let i = 0; i < scope._entries.length; i++) {
let entry = scope._entries[i];
if (entry instanceof _generator.TemporalOperationEntry) {
if (entry.declared === temporalAlias) {
return true;
}
}
}
return false;
}
_getReactLibraryValue() {
const reactLibraryObject = this.realm.fbLibraries.react;
(0, _invariant.default)(reactLibraryObject, "Unable to find React library reference in scope.");
return reactLibraryObject;
}
}
exports.ResidualReactElementVisitor = ResidualReactElementVisitor;
//# sourceMappingURL=ResidualReactElementVisitor.js.map