prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
516 lines (440 loc) • 24.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createAbstractFunction = createAbstractFunction;
exports.default = _default;
var _index = require("../../values/index.js");
var _singletons = require("../../singletons.js");
var _index2 = require("../../methods/index.js");
var _index3 = require("../../domains/index.js");
var _invariant = _interopRequireDefault(require("../../invariant.js"));
var _utils = require("./utils.js");
var _utils2 = require("../../utils.js");
var _utils3 = require("../../react/utils.js");
var _errors = require("../../errors.js");
var t = _interopRequireWildcard(require("@babel/types"));
var _generator = require("../../utils/generator.js");
var _ShapeInformation = require("../../utils/ShapeInformation");
var _descriptors = require("../../descriptors.js");
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
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.
*/
function createAbstractFunction(realm, ...additionalValues) {
return new _index.NativeFunctionValue(realm, "global.__abstract", "__abstract", 0, (context, [typeNameOrTemplate, _name, options]) => {
let name = _name;
if (name instanceof _index.StringValue) name = name.value;
if (name !== undefined && typeof name !== "string") {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "intrinsic name argument is not a string");
}
if (options && !(options instanceof _index.ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an ObjectValue if provided");
}
return (0, _utils.createAbstract)(realm, typeNameOrTemplate, name, options, ...additionalValues);
});
}
function _default(realm) {
let global = realm.$GlobalObject;
global.$DefineOwnProperty("__dump", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__dump", "__dump", 0, (context, args) => {
console.log("dump", args.map(arg => arg.serialize()));
return context;
}),
writable: true,
enumerable: false,
configurable: true
})); // Helper function to model values that are obtained from the environment,
// and whose concrete values are not known at Prepack-time.
// __abstract(typeNameOrTemplate, name, options) creates a new abstract value
// where typeNameOrTemplate can be...
// - 'string', 'boolean', 'number', 'object', 'function' or
// - ':string', ':boolean', ':number', ':object', ':function' to indicate that
// the abstract value represents a function that only returns values of the specified type, or
// - an actual object defining known properties.
// options is an optional object that may contain:
// - allowDuplicateNames: boolean representing whether the name of the abstract value may be
// repeated, by default they must be unique
// - disablePlaceholders: boolean representing whether placeholders should be substituted in
// the abstract value's name.
// If the abstract value gets somehow embedded in the final heap,
// it will be referred to by the supplied name in the generated code.
global.$DefineOwnProperty("__abstract", new _descriptors.PropertyDescriptor({
value: createAbstractFunction(realm),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__abstractOrNull", new _descriptors.PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.null),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__abstractOrNullOrUndefined", new _descriptors.PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.null, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__abstractOrUndefined", new _descriptors.PropertyDescriptor({
value: createAbstractFunction(realm, realm.intrinsics.undefined),
writable: true,
enumerable: false,
configurable: true
})); // Allows dynamically registering optimized functions.
// WARNING: these functions will get exposed at global scope and called there.
// NB: If we interpret one of these calls in an evaluateForEffects context
// that is not subsequently applied, the function will not be registered
// (because prepack won't have a correct value for the FunctionValue itself)
// If we encounter an invalid input, we will emit a warning and not optimize the function
global.$DefineOwnProperty("__optimize", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__optimize", "__optimize", 1, (context, [value, argModelString]) => {
let argModel;
if (argModelString !== undefined) {
argModel = (0, _ShapeInformation.createAndValidateArgModel)(realm, argModelString);
}
if (value instanceof _index.ECMAScriptSourceFunctionValue || value instanceof _index.AbstractValue) {
let currentArgModel = realm.optimizedFunctions.get(value); // Verify that if there is an existing argModel, that it is the same as the new one.
if (currentArgModel) {
let currentString = argModelString instanceof _index.StringValue ? argModelString.value : argModelString;
if (JSON.stringify(currentArgModel) !== currentString) {
let argModelError = new _errors.CompilerDiagnostic("__optimize called twice with different argModelStrings", realm.currentLocation, "PP1008", "Warning");
if (realm.handleError(argModelError) !== "Recover") throw new _errors.FatalError();else return realm.intrinsics.undefined;
}
}
realm.optimizedFunctions.set(value, argModel);
} else {
let location = value.expressionLocation ? `${value.expressionLocation.start.line}:${value.expressionLocation.start.column} ` + `${value.expressionLocation.end.line}:${value.expressionLocation.end.line}` : "location unknown";
let result = realm.handleError(new _errors.CompilerDiagnostic(`Optimized Function Value ${location} is an not a function or react element`, realm.currentLocation, "PP0033", "Warning"));
if (result !== "Recover") throw new _errors.FatalError();else return realm.intrinsics.undefined;
}
return value;
}),
writable: true,
enumerable: false,
configurable: true
}));
if (realm.react.enabled) {
global.$DefineOwnProperty("__reactComponentTrees", new _descriptors.PropertyDescriptor({
value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__reactComponentTrees",
/* refuseSerialization */
true),
writable: true,
enumerable: false,
configurable: true
}));
let reactComponentRootUid = 0;
global.$DefineOwnProperty("__optimizeReactComponentTree", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__optimizeReactComponentTree", "__optimizeReactComponentTree", 0, (context, [component, config]) => {
let hasValidComponent = component instanceof _index.ECMAScriptSourceFunctionValue || component instanceof _index.BoundFunctionValue || (0, _utils3.valueIsKnownReactAbstraction)(realm, component);
let hasValidConfig = config instanceof _index.ObjectValue || config === realm.intrinsics.undefined || config === undefined;
if (!hasValidComponent || !hasValidConfig) {
let diagnostic = new _errors.CompilerDiagnostic("__optimizeReactComponentTree(rootComponent, config) has been called with invalid arguments", realm.currentLocation, "PP0024", "FatalError");
realm.handleError(diagnostic);
if (realm.handleError(diagnostic) === "Fail") throw new _errors.FatalError();
}
let reactComponentTree = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype);
reactComponentTree.$Set("rootComponent", component, reactComponentTree);
reactComponentTree.$Set("config", config || realm.intrinsics.undefined, reactComponentTree);
realm.assignToGlobal(t.memberExpression(t.memberExpression(t.identifier("global"), t.identifier("__reactComponentTrees")), t.identifier("" + reactComponentRootUid++)), reactComponentTree);
return component;
}),
writable: true,
enumerable: false,
configurable: true
}));
}
global.$DefineOwnProperty("__evaluatePureFunction", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__evaluatePureFunction", "__evaluatePureFunction", 0, (context, [functionValue, callback]) => {
(0, _invariant.default)(functionValue instanceof _index.ECMAScriptSourceFunctionValue);
(0, _invariant.default)(typeof functionValue.$Call === "function");
let functionCall = functionValue.$Call;
return realm.evaluatePure(() => functionCall(realm.intrinsics.undefined, []),
/*bubbles*/
true,
/*reportSideEffectFunc*/
callback === undefined || callback === realm.intrinsics.undefined ? null : () => {
(0, _invariant.default)(callback instanceof _index.ECMAScriptSourceFunctionValue);
let call = callback.$Call;
(0, _invariant.default)(call !== undefined);
call(realm.intrinsics.undefined, []);
});
}),
writable: true,
enumerable: false,
configurable: true
})); // Maps from initialized moduleId to exports object
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty("__initializedModules", new _descriptors.PropertyDescriptor({
value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__initializedModules",
/* refuseSerialization */
true),
writable: true,
enumerable: false,
configurable: true
})); // Set of property bindings whose invariant got checked
// NB: Changes to this shouldn't ever be serialized
global.$DefineOwnProperty("__checkedBindings", new _descriptors.PropertyDescriptor({
value: new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, "__checkedBindings",
/* refuseSerialization */
true),
writable: true,
enumerable: false,
configurable: true
})); // Helper function used to instantiate a residual function
function createNativeFunctionForResidualCall(unsafe) {
return new _index.NativeFunctionValue(realm, "global.__residual", "__residual", 2, (context, [typeNameOrTemplate, f, ...args]) => {
if (!realm.useAbstractInterpretation) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
}
let {
type,
template
} = (0, _utils.parseTypeNameOrTemplate)(realm, typeNameOrTemplate);
if (!_index.Value.isTypeCompatibleWith(f.constructor, _index.FunctionValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "cannot determine residual function");
}
(0, _invariant.default)(f instanceof _index.FunctionValue);
f.isResidual = true;
if (unsafe) f.isUnsafeResidual = true;
let result = _index.AbstractValue.createTemporalFromBuildFunction(realm, type, [f].concat(args), (0, _generator.createOperationDescriptor)("RESIDUAL_CALL"));
if (template) {
(0, _invariant.default)(result instanceof _index.AbstractValue, "the nested properties should only be rebuilt for an abstract value");
template.makePartial();
result.values = new _index3.ValuesDomain(new Set([template]));
(0, _invariant.default)(realm.generator);
realm.rebuildNestedProperties(result, result.getIdentifier());
}
return result;
});
}
function createNativeFunctionForResidualInjection(name, initializeAndValidateArgs, operationDescriptor, numArgs) {
return new _index.NativeFunctionValue(realm, "global." + name, name, numArgs, (context, ciArgs) => {
initializeAndValidateArgs(ciArgs);
(0, _invariant.default)(realm.generator !== undefined);
realm.generator.emitStatement(ciArgs, operationDescriptor);
return realm.intrinsics.undefined;
});
} // Helper function that specifies a dynamic invariant that cannot be evaluated at prepack time, and needs code to
// be injected into the serialized output.
global.$DefineOwnProperty("__assume", new _descriptors.PropertyDescriptor({
value: createNativeFunctionForResidualInjection("__assume", ([c, s]) => {
if (!c.mightBeTrue()) {
let error = new _errors.CompilerDiagnostic(`Assumed condition cannot hold`, realm.currentLocation, "PP0040", "FatalError");
realm.handleError(error);
throw new _errors.FatalError();
}
_singletons.Path.pushAndRefine(c);
}, (0, _generator.createOperationDescriptor)("ASSUME_CALL"), 2),
writable: true,
enumerable: false,
configurable: true
})); // Helper function for Prepack developers inspect a value
// when interpreting a particular node in the AST.
global.$DefineOwnProperty("__debugValue", new _descriptors.PropertyDescriptor({
value: createNativeFunctionForResidualInjection("__debugValue", ([v, s]) => {
debugger; // eslint-disable-line no-debugger
}, (0, _generator.createOperationDescriptor)("NOOP"), 2),
writable: true,
enumerable: false,
configurable: true
})); // Helper function that identifies a computation that must remain part of the residual program and cannot be partially evaluated,
// e.g. because it contains a loop over abstract values.
// __residual(typeNameOrTemplate, function, arg0, arg1, ...) creates a new abstract value
// that is computed by invoking function(arg0, arg1, ...) in the residual program and
// where typeNameOrTemplate either either 'string', 'boolean', 'number', 'object', or an actual object defining known properties.
// The function must not have side effects, and it must not access any state (besides the supplied arguments).
global.$DefineOwnProperty("__residual", new _descriptors.PropertyDescriptor({
value: createNativeFunctionForResidualCall(false),
writable: true,
enumerable: false,
configurable: true
})); // Helper function that identifies a variant of the residual function that has implicit dependencies. This version of residual will infer the dependencies
// and rewrite the function body to do the same thing as the original residual function.
global.$DefineOwnProperty("__residual_unsafe", new _descriptors.PropertyDescriptor({
value: createNativeFunctionForResidualCall(true),
writable: true,
enumerable: false,
configurable: true
})); // Internal helper function for tests.
// __isAbstract(value) checks if a given value is abstract.
global.$DefineOwnProperty("__isAbstract", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__isAbstract", "__isAbstract", 1, (context, [value]) => {
return new _index.BooleanValue(realm, value instanceof _index.AbstractValue);
}),
writable: true,
enumerable: false,
configurable: true
})); // __makePartial(object) marks an (abstract) object as partial.
global.$DefineOwnProperty("__makePartial", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__makePartial", "__makePartial", 1, (context, [object]) => {
if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
object.makePartial();
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__makeFinal", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__makeFinal", "__makeFinal", 1, (context, [object]) => {
if (object instanceof _index.ObjectValue || object instanceof _index.AbstractObjectValue && !object.values.isTop()) {
object.makeFinal();
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an object or abstract object value (non-top)");
}),
writable: true,
enumerable: false,
configurable: true
})); // __makeSimple(object) marks an (abstract) object as one that has no getters or setters.
global.$DefineOwnProperty("__makeSimple", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__makeSimple", "__makeSimple", 1, (context, [object, option]) => {
if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
object.makeSimple(option);
return object;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true
})); // Helper function that emits a check whether a given object property has a particular value.
global.$DefineOwnProperty("__assumeDataProperty", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__assumeDataProperty", "__assumeDataProperty", 3, (context, [object, propertyName, value, invariantOptions]) => {
if (!realm.useAbstractInterpretation) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "realm is not partial");
}
if (object instanceof _index.AbstractObjectValue || object instanceof _index.ObjectValue) {
let generator = realm.generator;
(0, _invariant.default)(generator);
let key = _singletons.To.ToStringPartial(realm, propertyName);
if (realm.emitConcreteModel) {
generator.emitConcreteModel(key, value);
} else if (realm.invariantLevel >= 1) {
let invariantOptionString = invariantOptions ? _singletons.To.ToStringPartial(realm, invariantOptions) : "FULL_INVARIANT";
switch (invariantOptionString) {
// checks (!property in object || object.property === undefined)
case "VALUE_DEFINED_INVARIANT":
generator.emitPropertyInvariant(object, key, value.mightBeUndefined() ? "PRESENT" : "DEFINED");
break;
case "SKIP_INVARIANT":
break;
case "FULL_INVARIANT":
generator.emitFullInvariant(object, key, value);
break;
default:
(0, _invariant.default)(false, "Invalid invariantOption " + invariantOptionString);
}
if (!realm.neverCheckProperty(object, key)) realm.markPropertyAsChecked(object, key);
}
realm.generator = undefined; // don't emit code during the following $Set call
// casting to due to Flow workaround above
object.$Set(key, value, object);
realm.generator = generator;
if (object.intrinsicName) realm.rebuildObjectProperty(object, key, value, object.intrinsicName);
return context.$Realm.intrinsics.undefined;
}
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not an (abstract) object");
}),
writable: true,
enumerable: false,
configurable: true
})); // Helper function that replaces the implementation of a source function with
// the details from another source function body, including the captured
// environment, the actual code, etc.
// This realizes a form of monkey-patching, enabling mocking a function if
// one doesn't control all existing references to that function,
// or if the storage location to those references cannot be easily updated.
// NOTE: This function affects un-tracked state, so care must be taken
// that this helper function is executed at the right time; typically, one
// would want to execute this function before any call is executed to that
// function. Care must be taken not to make reachable conditionally
// defined values. Because of this limitations, this helper function
// should be considered only as a last resort.
global.$DefineOwnProperty("__replaceFunctionImplementation_unsafe", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__replaceFunctionImplementation_unsafe", "__replaceFunctionImplementation_unsafe", 2, (context, [target, source]) => {
if (!(target instanceof _index.ECMAScriptSourceFunctionValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "first argument is not a function with source code");
}
if (!(source instanceof _index.ECMAScriptSourceFunctionValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "second argument is not a function with source code");
} // relevant properties for functionValue
target.$Environment = source.$Environment;
target.$ScriptOrModule = source.$ScriptOrModule; // properties for ECMAScriptFunctionValue
target.$ConstructorKind = source.$ConstructorKind;
target.$ThisMode = source.$ThisMode;
target.$HomeObject = source.$HomeObject;
target.$FunctionKind = source.$FunctionKind; // properties for ECMAScriptSourceFunctionValue
target.$Strict = source.$Strict;
target.$FormalParameters = source.$FormalParameters;
target.$ECMAScriptCode = source.$ECMAScriptCode;
target.$HasComputedName = source.$HasComputedName;
target.$HasEmptyConstructor = source.$HasEmptyConstructor;
target.loc = source.loc;
return context.$Realm.intrinsics.undefined;
}),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__IntrospectionError", new _descriptors.PropertyDescriptor({
value: realm.intrinsics.__IntrospectionError,
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__isIntegral", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__isIntegral", "__isIntegral", 1, (context, [value]) => {
return new _index.BooleanValue(realm, value instanceof _index.IntegralValue);
}),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__describe", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__describe", "__describe", 1, (context, [value]) => {
return new _index.StringValue(realm, (0, _utils2.describeValue)(value));
}),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__fatal", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__fatal", "__fatal", 0, (context, []) => {
throw new _errors.FatalError();
}),
writable: true,
enumerable: false,
configurable: true
}));
global.$DefineOwnProperty("__eagerlyRequireModuleDependencies", new _descriptors.PropertyDescriptor({
value: new _index.NativeFunctionValue(realm, "global.__eagerlyRequireModuleDependencies", "__eagerlyRequireModuleDependencies", 1, (context, [functionValue]) => {
if (!(0, _index2.IsCallable)(realm, functionValue) || !(functionValue instanceof _index.FunctionValue)) throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be callable function");
let functionCall = functionValue.$Call;
if (typeof functionCall !== "function") {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "argument must be directly callable");
}
let old = realm.eagerlyRequireModuleDependencies;
realm.eagerlyRequireModuleDependencies = true;
try {
return functionCall(realm.intrinsics.undefined, []);
} finally {
realm.eagerlyRequireModuleDependencies = old;
}
}),
writable: true,
enumerable: false,
configurable: true
}));
}
//# sourceMappingURL=global.js.map