UNPKG

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
"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