prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
291 lines (215 loc) • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Serializer = void 0;
var _realm = require("../realm.js");
var _errors = require("../errors.js");
var _types = require("../types.js");
var _completions = require("../completions.js");
var _generator = require("../utils/generator.js");
var _generator2 = _interopRequireDefault(require("@babel/generator"));
var _traverseFast = _interopRequireDefault(require("../utils/traverse-fast.js"));
var _invariant = _interopRequireDefault(require("../invariant.js"));
var _statistics = require("./statistics.js");
var _types2 = require("./types.js");
var _functions = require("./functions.js");
var _logger = require("../utils/logger.js");
var _modules = require("../utils/modules.js");
var _flow = require("../utils/flow.js");
var _LoggingTracer = require("./LoggingTracer.js");
var _ResidualHeapVisitor = require("./ResidualHeapVisitor.js");
var _ResidualHeapSerializer = require("./ResidualHeapSerializer.js");
var _ResidualHeapValueIdentifiers = require("./ResidualHeapValueIdentifiers.js");
var _LazyObjectsSerializer = require("./LazyObjectsSerializer.js");
var t = _interopRequireWildcard(require("@babel/types"));
var _ResidualHeapRefCounter = require("./ResidualHeapRefCounter");
var _ResidualHeapGraphGenerator = require("./ResidualHeapGraphGenerator");
var _Referentializer = require("./Referentializer.js");
var _index = require("../methods/index.js");
var _index2 = require("../values/index.js");
var _singletons = require("../singletons.js");
var _descriptors = require("../descriptors.js");
var _ResidualOptimizedFunctions = require("./ResidualOptimizedFunctions");
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.
*/
class Serializer {
constructor(realm, serializerOptions = {}) {
(0, _invariant.default)(realm.useAbstractInterpretation); // Start tracking mutations
realm.generator = new _generator.Generator(realm, "main", realm.pathConditions);
this.realm = realm;
this.logger = new _logger.Logger(this.realm, !!serializerOptions.internalDebug);
this.modules = new _modules.Modules(this.realm, this.logger, !!serializerOptions.logModules);
this.functions = new _functions.Functions(this.realm, this.modules.moduleTracer);
if (serializerOptions.trace) {
let loggingTracer = new _LoggingTracer.LoggingTracer(this.realm);
this.realm.tracers.push(loggingTracer);
}
this.options = serializerOptions;
}
_execute(sourceFileCollection, sourceMaps = false, onParse) {
let realm = this.realm;
let [res, code] = realm.$GlobalEnv.executeSources(sourceFileCollection.toArray(), "script", ast => {
let realmPreludeGenerator = realm.preludeGenerator;
(0, _invariant.default)(realmPreludeGenerator);
let forbiddenNames = realmPreludeGenerator.nameGenerator.forbiddenNames;
(0, _traverseFast.default)(ast, node => {
if (!t.isIdentifier(node)) return false;
forbiddenNames.add(node.name);
return true;
});
if (onParse) onParse(ast);
}); // Release memory of source files and their source maps
sourceFileCollection.destroy();
if (res instanceof _completions.AbruptCompletion) {
let context = new _realm.ExecutionContext();
realm.pushContext(context);
try {
this.logger.logCompletion(res);
} finally {
realm.popContext(context);
}
let diagnostic = new _errors.CompilerDiagnostic("Global code may end abruptly", res.location, "PP0016", "FatalError");
realm.handleError(diagnostic);
throw new _errors.FatalError();
}
return code;
} // When global.__output is an object, then this function replaces the global generator
// with one that declares global variables corresponding to the key-value pairs in the __output object,
// effectively rewriting the roots for visiting/serialization.
processOutputEntries() {
let realm = this.realm;
let output = this.logger.tryQuery(() => (0, _index.Get)(realm, realm.$GlobalObject, "__output"), realm.intrinsics.undefined);
if (!(output instanceof _index2.ObjectValue)) return false;
let generator = realm.generator;
let preludeGenerator = realm.preludeGenerator;
if (generator === undefined || preludeGenerator === undefined) return false;
generator._entries.length = 0;
preludeGenerator.declaredGlobals.clear();
for (let name of _singletons.Properties.GetOwnPropertyKeysArray(realm, output, false, false)) {
let property = output.properties.get(name);
if (!property) continue;
let value = property.descriptor instanceof _descriptors.PropertyDescriptor && property.descriptor.value;
if (!(value instanceof _index2.Value)) continue;
generator.emitGlobalDeclaration(name, value);
}
return true;
}
init(sourceFileCollection, sourceMaps = false, onParse, onExecute) {
let realmStatistics = this.realm.statistics;
(0, _invariant.default)(realmStatistics instanceof _statistics.SerializerStatistics, "serialization requires SerializerStatistics");
let statistics = realmStatistics;
let result = statistics.total.measure(() => {
// Phase 1: Let's interpret.
if (this.realm.react.verbose) {
this.logger.logInformation(`Evaluating initialization path...`);
}
let code = this._execute(sourceFileCollection, sourceMaps, onParse);
if (this.logger.hasErrors()) return undefined;
if (!this.processOutputEntries()) {
statistics.resolveInitializedModules.measure(() => this.modules.resolveInitializedModules());
}
statistics.checkThatFunctionsAreIndependent.measure(() => this.functions.checkThatFunctionsAreIndependent());
let reactStatistics;
if (this.realm.react.enabled) {
statistics.optimizeReactComponentTreeRoots.measure(() => {
reactStatistics = new _types2.ReactStatistics();
this.functions.optimizeReactComponentTreeRoots(reactStatistics);
});
}
statistics.processCollectedNestedOptimizedFunctions.measure(() => this.functions.processCollectedNestedOptimizedFunctions());
statistics.dumpIR.measure(() => {
if (onExecute !== undefined) {
let optimizedFunctions = new Map();
for (let [functionValue, additionalFunctionEffects] of this.functions.getAdditionalFunctionValuesToEffects()) optimizedFunctions.set(functionValue, additionalFunctionEffects.generator);
onExecute(this.realm, optimizedFunctions);
}
});
let modulesToInitialize = this.options.modulesToInitialize;
if (modulesToInitialize) {
statistics.modulesToInitialize.measure(() => this.modules.initializeMoreModules(modulesToInitialize));
if (this.logger.hasErrors()) return undefined;
}
let heapGraph;
let ast = (() => {
// We wrap the following in an anonymous function declaration to ensure
// that all local variables are locally scoped, and allocated memory cannot
// get released when this function returns.
let additionalFunctionValuesAndEffects = this.functions.getAdditionalFunctionValuesToEffects(); // Deep traversal of the heap to identify the necessary scope of residual functions
let preludeGenerator = this.realm.preludeGenerator;
(0, _invariant.default)(preludeGenerator !== undefined);
if (this.realm.react.verbose) {
this.logger.logInformation(`Visiting evaluated nodes...`);
}
let [residualHeapInfo, generatorTree, inspector] = (() => {
let residualHeapVisitor = new _ResidualHeapVisitor.ResidualHeapVisitor(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects);
statistics.deepTraversal.measure(() => residualHeapVisitor.visitRoots());
return [residualHeapVisitor.toInfo(), residualHeapVisitor.generatorTree, residualHeapVisitor.inspector];
})();
if (this.logger.hasErrors()) return undefined;
let residualOptimizedFunctions = new _ResidualOptimizedFunctions.ResidualOptimizedFunctions(generatorTree, additionalFunctionValuesAndEffects, residualHeapInfo.values);
let referentializer = new _Referentializer.Referentializer(this.realm, this.options, preludeGenerator.createNameGenerator("__scope_"), preludeGenerator.createNameGenerator("__get_scope_binding_"), preludeGenerator.createNameGenerator("__leaked_"), residualOptimizedFunctions);
statistics.referentialization.measure(() => {
for (let instance of residualHeapInfo.functionInstances.values()) referentializer.referentialize(instance);
});
if (this.realm.react.verbose) {
this.logger.logInformation(`Serializing evaluated nodes...`);
}
const realmPreludeGenerator = this.realm.preludeGenerator;
(0, _invariant.default)(realmPreludeGenerator);
const residualHeapValueIdentifiers = new _ResidualHeapValueIdentifiers.ResidualHeapValueIdentifiers(residualHeapInfo.values.keys(), realmPreludeGenerator);
if (this.options.heapGraphFormat) {
const heapRefCounter = new _ResidualHeapRefCounter.ResidualHeapRefCounter(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects);
heapRefCounter.visitRoots();
const heapGraphGenerator = new _ResidualHeapGraphGenerator.ResidualHeapGraphGenerator(this.realm, this.logger, this.modules, additionalFunctionValuesAndEffects, residualHeapValueIdentifiers, heapRefCounter.getResult());
heapGraphGenerator.visitRoots();
(0, _invariant.default)(this.options.heapGraphFormat);
heapGraph = heapGraphGenerator.generateResult(this.options.heapGraphFormat);
} // Phase 2: Let's serialize the heap and generate code.
// Serialize for the first time in order to gather reference counts
if (this.options.inlineExpressions) {
residualHeapValueIdentifiers.initPass1();
statistics.referenceCounts.measure(() => {
new _ResidualHeapSerializer.ResidualHeapSerializer(this.realm, this.logger, this.modules, residualHeapValueIdentifiers, inspector, residualHeapInfo, this.options, additionalFunctionValuesAndEffects, referentializer, generatorTree, residualOptimizedFunctions).serialize();
});
if (this.logger.hasErrors()) return undefined;
residualHeapValueIdentifiers.initPass2();
} // Serialize for a second time, using reference counts to minimize number of generated identifiers
const TargetSerializer = this.options.lazyObjectsRuntime != null ? _LazyObjectsSerializer.LazyObjectsSerializer : _ResidualHeapSerializer.ResidualHeapSerializer;
statistics.resetBeforePass();
return statistics.serializePass.measure(() => new TargetSerializer(this.realm, this.logger, this.modules, residualHeapValueIdentifiers, inspector, residualHeapInfo, this.options, additionalFunctionValuesAndEffects, referentializer, generatorTree, residualOptimizedFunctions).serialize());
})();
(0, _invariant.default)(ast !== undefined);
if (this.realm.stripFlow) {
(0, _flow.stripFlowTypeAnnotations)(ast);
} // the signature for generate is not complete, hence the any
let generated = statistics.babelGenerate.measure(() => (0, _generator2.default)(ast, {
sourceMaps: sourceMaps
}, code));
(0, _invariant.default)(!this.logger.hasErrors());
return {
code: generated.code,
map: generated.map,
statistics,
reactStatistics,
heapGraph
};
});
if (this.options.logStatistics) {
statistics.log();
statistics.logSerializerPerformanceTrackers("time statistics", statistics.forcingGC ? "Time statistics are skewed because of forced garbage collections; remove --expose-gc flag from node.js invocation to disable forced garbage collections." : undefined, pf => `${pf.time}ms`);
statistics.logSerializerPerformanceTrackers("memory statistics", statistics.forcingGC ? undefined : "Memory statistics are unreliable because garbage collections could not be forced; pass --expose-gc to node.js to enable forced garbage collections.", pf => `${pf.memory > 0 ? "+" : ""}${Math.round(pf.memory / 1024 / 1024)}MB`);
}
return result;
}
}
exports.Serializer = Serializer;
//# sourceMappingURL=serializer.js.map