UNPKG

prepack

Version:

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

381 lines (306 loc) 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = function (realm) { let intrinsicName = 'process.binding("contextify")'; let obj = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, intrinsicName); // Contextify function runInDebugContextImpl(code) { // TODO: Make this an abstract result. throw realm.createErrorThrowCompletion(realm.intrinsics.Error, "The V8 debugger is not available from within Prepack."); } function makeContextImpl() { // TODO: Allow sub-realms to be created and restored. throw realm.createErrorThrowCompletion(realm.intrinsics.Error, "makeContext is not yet implemented in Prepack."); } function isContextImpl() { // TODO: We don't have a way to create contexts so this is always false. return realm.intrinsics.false; } // ContextifyScript class ContextifyScriptInternal { constructor(ast) { this.ast = ast; } } function ContextifyScriptConstructor(context, args, argLength, newTarget) { if (!newTarget) { throw realm.createErrorThrowCompletion(realm.intrinsics.Error, "Must call vm.Script as a constructor."); } let proto = (0, _index2.Get)(realm, newTarget, "prototype"); if (!(proto instanceof _index.ObjectValue)) { realm = (0, _index2.GetFunctionRealm)(realm, newTarget); proto = ContextifyScriptPrototype; } (0, _invariant2.default)(args[0] instanceof _index.ConcreteValue); let code = _singletons.To.ToString(realm, args[0]); let options = args[1]; let filename = getFilenameArg(options); let lineOffset = getLineOffsetArg(options); let columnOffset = getColumnOffsetArg(options); let displayErrors = getDisplayErrorsArg(options); let cachedDataBuf = getCachedData(options); let produceCachedData = getProduceCachedData(options); let resolvedOptions = { filename: filename, lineOffset: lineOffset, columnOffset: columnOffset, displayErrors: displayErrors, cachedDataBuf: undefined, // Not serializable. produceCachedData: produceCachedData }; let intrinsicConstructor = `new (${intrinsicName}).ContextifyScript(${JSON.stringify(code)}, ${JSON.stringify(resolvedOptions)})`; let self = new _index.ObjectValue(realm, proto, intrinsicConstructor); if (cachedDataBuf.length) { _singletons.Properties.Set(realm, obj, "cachedDataRejected", realm.intrinsics.true, true); } if (produceCachedData) { _singletons.Properties.Set(realm, obj, "cachedDataProduced", realm.intrinsics.false, true); } let ast; try { // TODO: Somehow pass columnOffset to Babylon. ast = (0, _parse2.default)(realm, transform(code, filename), filename, "script", 1 + lineOffset); } catch (e) { if (displayErrors && e instanceof _completions.ThrowCompletion) { decorateErrorStack(e); } throw e; } // TODO: Pick up source map files and automatically fix up source locations. self.$InternalSlot = new ContextifyScriptInternal(ast); return self; } let runInDebugContext = new _index.NativeFunctionValue(realm, `${intrinsicName}.runInDebugContext`, "runInDebugContext", 0, runInDebugContextImpl); _singletons.Properties.Set(realm, obj, "runInDebugContext", runInDebugContext, true); let makeContext = new _index.NativeFunctionValue(realm, `${intrinsicName}.makeContext`, "makeContext", 0, makeContextImpl); _singletons.Properties.Set(realm, obj, "makeContext", makeContext, true); let isContext = new _index.NativeFunctionValue(realm, `${intrinsicName}.isContext`, "isContext", 0, isContextImpl); _singletons.Properties.Set(realm, obj, "isContext", isContext, true); let ContextifyScript = new _index.NativeFunctionValue(realm, `${intrinsicName}.ContextifyScript`, "ContextifyScript", 0, ContextifyScriptConstructor, true); _singletons.Properties.Set(realm, obj, "ContextifyScript", ContextifyScript, true); // ContextifyScript.prototype function runInThisContext(self, args) { let timeout = getTimeoutArg(args[0]); let displayErrors = getDisplayErrorsArg(args[0]); let breakOnSigint = getBreakOnSigintArg(args[0]); return evalMachine(self, timeout, displayErrors, breakOnSigint); } function runInContext(self, [sandbox, options]) { throw realm.createErrorThrowCompletion(realm.intrinsics.Error, "Cannot run in arbitrary contexts within Prepack yet."); } function decorateErrorStack(completion) { let error = completion.value; if (!(error instanceof _index.ObjectValue)) { return; } let errorData = error.$ErrorData; if (!errorData) { return; } let errorLocation = errorData.locationData; if (!errorLocation || errorLocation.stackDecorated) { return; } let stack = (0, _index2.Get)(realm, error, "stack"); if (!(stack instanceof _index.StringValue)) { return; } let lines = errorLocation.sourceCode.split(/\r?\n/); let line = lines[errorLocation.loc.line - 1] || ""; let arrow = " ".repeat(errorLocation.loc.column) + "^"; let decoratedStack = `${errorLocation.filename}:${errorLocation.loc.line}\n${line}\n${arrow}\n${stack.value}`; _singletons.Properties.Set(realm, error, "stack", new _index.StringValue(realm, decoratedStack), false); errorLocation.stackDecorated = true; } function getBreakOnSigintArg(options) { if (options instanceof _index.UndefinedValue || options instanceof _index.StringValue) { return false; } if (!(options instanceof _index.ObjectValue)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object"); } let value = (0, _index2.Get)(realm, options, "breakOnSigint"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); return value instanceof _index.BooleanValue && value.value; } function getTimeoutArg(options) { if (options instanceof _index.UndefinedValue || options instanceof _index.StringValue) { return -1; } if (!(options instanceof _index.ObjectValue)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object"); } let value = (0, _index2.Get)(realm, options, "timeout"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); if (value instanceof _index.UndefinedValue) { return -1; } let timeout = _singletons.To.ToInteger(realm, value); if (timeout <= 0) { throw realm.createErrorThrowCompletion(realm.intrinsics.RangeError, "timeout must be a positive number"); } return timeout; } function getDisplayErrorsArg(options) { if (options instanceof _index.UndefinedValue || options instanceof _index.StringValue) { return true; } if (!(options instanceof _index.ObjectValue)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object"); } let value = (0, _index2.Get)(realm, options, "displayErrors"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); if (value instanceof _index.UndefinedValue) { return true; } return _singletons.To.ToBoolean(realm, value); } function getFilenameArg(options) { const defaultFilename = "evalmachine.<anonymous>"; if (options instanceof _index.UndefinedValue) { return defaultFilename; } if (options instanceof _index.StringValue) { return options.value; } if (!(options instanceof _index.ObjectValue)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object"); } let value = (0, _index2.Get)(realm, options, "filename"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); if (value instanceof _index.UndefinedValue) { return defaultFilename; } return _singletons.To.ToString(realm, value); } function getCachedData(options) { if (!(options instanceof _index.ObjectValue)) { return new Uint8Array(0); } let value = (0, _index2.Get)(realm, options, "cachedData"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); if (value instanceof _index.UndefinedValue) { return new Uint8Array(0); } if (!value.$ViewedArrayBuffer || !(value.$ViewedArrayBuffer.$ArrayBufferData instanceof Uint8Array)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options.cachedData must be a Buffer instance"); } return value.$ViewedArrayBuffer.$ArrayBufferData; } function getProduceCachedData(options) { if (!(options instanceof _index.ObjectValue)) { return false; } let value = (0, _index2.Get)(realm, options, "produceCachedData"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); return value instanceof _index.BooleanValue && value.value; } function getLineOffsetArg(options) { const defaultLineOffset = 0; if (!(options instanceof _index.ObjectValue)) { return defaultLineOffset; } let value = (0, _index2.Get)(realm, options, "lineOffset"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); return value instanceof _index.UndefinedValue ? defaultLineOffset : _singletons.To.ToInteger(realm, value); } function getColumnOffsetArg(options) { const defaultColumnOffset = 0; if (!(options instanceof _index.ObjectValue)) { return defaultColumnOffset; } let value = (0, _index2.Get)(realm, options, "columnOffset"); (0, _invariant2.default)(value instanceof _index.ConcreteValue); return value instanceof _index.UndefinedValue ? defaultColumnOffset : _singletons.To.ToInteger(realm, value); } function evalMachine(self, timeout, displayErrors, breakOnSigint) { if (!(self instanceof _index.ObjectValue) || !(self.$InternalSlot instanceof ContextifyScriptInternal)) { throw realm.createErrorThrowCompletion(realm.intrinsics.Error, "Script methods can only be called on script instances."); } let script = self.$InternalSlot; let environment = realm.$GlobalEnv; let previousContext = realm.getRunningContext(); previousContext.suspend(); let context = realm.createExecutionContext(); context.lexicalEnvironment = environment; context.variableEnvironment = environment; context.realm = realm; realm.pushContext(context); let result; try { result = environment.evaluateCompletion(script.ast, false); } finally { context.suspend(); realm.popContext(context); (0, _invariant2.default)(context.lexicalEnvironment === realm.$GlobalEnv); realm.onDestroyScope(context.lexicalEnvironment); } (0, _invariant2.default)(realm.getRunningContext() === previousContext); previousContext.resume(); if (result instanceof _index.EmptyValue) { return realm.intrinsics.undefined; } else if (result instanceof _index.Value) { return result; } else { (0, _invariant2.default)(result instanceof _completions.AbruptCompletion); if (displayErrors) { decorateErrorStack(result); } throw result; } } let ContextifyScriptPrototype = new _index.ObjectValue(realm, realm.intrinsics.ObjectPrototype, `${intrinsicName}.ContextifyScript.prototype`); ContextifyScriptPrototype.defineNativeMethod("runInContext", 2, runInContext); ContextifyScriptPrototype.defineNativeMethod("runInThisContext", 1, runInThisContext); _singletons.Properties.DefinePropertyOrThrow(realm, ContextifyScript, "prototype", { value: ContextifyScriptPrototype, writable: true, enumerable: false, configurable: false }); _singletons.Properties.DefinePropertyOrThrow(realm, ContextifyScriptPrototype, "constructor", { value: ContextifyScript, writable: true, enumerable: false, configurable: true }); return obj; }; var _invariant = require("../../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _realm = require("../../realm.js"); var _completions = require("../../completions.js"); var _index = require("../../values/index.js"); var _index2 = require("../../methods/index.js"); var _singletons = require("../../singletons.js"); var _parse = require("../../utils/parse.js"); var _parse2 = _interopRequireDefault(_parse); var _babelCore = require("babel-core"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Hook for transpiling /** * 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 transform(code, filename) { let patchedCode = code.replace( // Work around the fact that Babel classes can't extend natives. /class FastBuffer extends Uint8Array {\s+constructor\(arg1, arg2, arg3\) {\s+super\(arg1, arg2, arg3\);\s+}\s+}/g, "function FastBuffer(arg1, arg2, arg3) {\n" + " var self = new Uint8Array(arg1, arg2, arg3);\n" + " Object.setPrototypeOf(self, FastBuffer.prototype);\n" + " return self;\n" + "}; Object.setPrototypeOf(FastBuffer, Uint8Array); Object.setPrototypeOf(FastBuffer.prototype, Uint8Array.prototype);"); let transformedCode = (0, _babelCore.transform)(patchedCode, { plugins: [ // Prepack doesn't support classes or destructuring yet. "transform-es2015-classes", "transform-es2015-destructuring", "transform-es2015-parameters"], retainLines: true }); return transformedCode.code; } // TODO: This creates a strong dependency on babel and its transforms even // outside of devDependencies which is unfortunate. Get rid of this once classes // and destructuring is fully implemented. //# sourceMappingURL=contextify.js.map