Delete node-cli Option and all the Node.js intrinsics (#2267)

Summary:
Since I'm adding a new experiment I figured I'd delete an equivalent sized one.

Last year I added an option that runs the Prepack program by invoking Node.js JS runtime which lets us prepack the whole module system and initialization. It's essentially a packager with perfect Node.js module resolution semantics. It did this by modeling Node's native environment as Prepack bindings.

This PR removes that whole option.

There's a few reasons why I don't think that worked out as a good idea.

- It's not solving a real need. It is hard to keep different module systems in tact. There is always something in the ecosystem that breaks down and using the canonical one solves that. However, in practice, if there is a need for bundling the ecosystem itself adapts to the toolchain. So it's not actually that hard to bundle up a CLI even with Webpack, even if it's strictly not 100% compatible, by tweaking a few downstream depenencies.

- Running the resulting bundle is tricky. The resulting bundle includes the JS parts of Node. This overlaps with what Node.js adds at runtime so it runs it twice. The ideal is actually to build a custom distribution of Node.js but this is generally overkill for what people want.

- Bindings change a lot. While Node.js's API notoriously doesn't change much. The internals do change a lot. By picking the API boundary in the middle of the internals of Node.js, it risks changing with any version. While technically observable changes, nobody else relies on these details. If this option was worth its weight, someone could probably maintain it but so far that has not been the case so we had to disable this option in CI to upgrade Node.

However, going forward I think there are alternative approaches we can explore.

- First class module system. This is something we really need at some point. A first class module system would be able to load Node.js module files from disk and package them up while excluding others. It doesn't have to be literally Node.js's module system. Close enough is ok. Especially as standards compliant ECMAScript modules get more popular. This lets us target compiling output that runs after Node's initialization.

- By introducing havocing and membranes in the boundaries, it becomes possible to initialize Node.js modules without actually knowing the internal of the boundaries.

- We've started optimizing residual functions which is much more interesting. However, this requires that code puts some constraints on how it works with its environment. It's not designed to be fully backwards compatible. That's probably a good thing but that also means that we can put constraints on the modules being Prepacked.

This removes the ability to prepack Prepack itself which is unfortunate but already wasn't being tested. To speed up Prepack itself, the [LLVM backend](https://github.com/facebook/prepack/pull/2264) seems much more useful if it can ever work on Prepack itself.
Pull Request resolved: https://github.com/facebook/prepack/pull/2267

Differential Revision: D8863788

Pulled By: sebmarkbage

fbshipit-source-id: d777ec9a95c8523b3386cfad553d9f691ec59074
This commit is contained in:
Sebastian Markbage 2018-07-16 12:54:37 -07:00 committed by Facebook Github Bot
parent 51e495a4e2
commit 81bc21fad8
18 changed files with 4 additions and 1655 deletions

View File

@ -42,7 +42,6 @@
"test-internal-react": "babel-node --stack_size=10000 --stack_trace_limit=200 --max_old_space_size=16384 scripts/test-internal-react.js",
"test-error-handler": "babel-node scripts/test-error-handler.js",
"test-error-handler-with-coverage": "./node_modules/.bin/istanbul cover ./lib/test-error-handler.js --dir coverage.error && ./node_modules/.bin/remap-istanbul -i coverage.error/coverage.json -o coverage-sourcemapped.error -t html",
"test-node-cli-mode": "bash < scripts/test-node-cli-mode.sh",
"test-std-in": "bash < scripts/test-std-in.sh",
"test-react": "jest",
"test-react-fast": "SKIP_REACT_JSX_TESTS=true jest",
@ -52,7 +51,6 @@
"repl": "node lib/repl-cli.js",
"precheck": "yarn prepack-cli --check",
"prepack-cli": "node --stack_size=10000 --stack_trace_limit=200 --max_old_space_size=16384 lib/prepack-cli.js --accelerateUnsupportedRequires --compatibility jsc-600-1-4-17 --delayUnsupportedRequires --mathRandomSeed 0",
"prepack-prepack": "node --stack_size=10000 --max_old_space_size=8096 ./bin/prepack.js ./lib/prepack-cli.js --out ./lib/prepack-cli.prepacked.js --compatibility node-cli --mathRandomSeed rnd",
"validate": "yarn install --frozen-lockfile && yarn build && yarn build-scripts && yarn lint && yarn depcheck && yarn flow && yarn test",
"prepublishOnly": "yarn build",
"depcheck": "babel-node scripts/detect_bad_deps.js",

View File

@ -1,17 +0,0 @@
# Prepack test input
node ./bin/prepack.js ./test/node-cli/Simple.js --out Simple-test.js --compatibility node-cli
# Run the resulting program and check it for expected output
node ./Simple-test.js | grep "Hey world" > /dev/null
if [[ $? -ne 0 ]]; then
exit 1
fi
rm ./Simple-test.js
# Prepack test input
node ./bin/prepack.js ./test/node-cli/FileSystem.js --out FileSystem-test.js --compatibility node-cli
# Run the resulting program and check it for expected output
node ./FileSystem-test.js | grep "Hey Hello world" > /dev/null
if [[ $? -ne 0 ]]; then
exit 1
fi
rm ./FileSystem-test.js

View File

@ -1,35 +0,0 @@
/**
* 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.
*/
/* @flow */
import { FatalError } from "../../errors.js";
import type { Realm } from "../../realm.js";
import { FunctionValue } from "../../values/index.js";
declare var process: any;
export default function(realm: Realm): FunctionValue {
// Extract the bootstrap source code from the hosting Node version.
let nodeSourceCode = process.binding("natives");
let bootstrapSource = nodeSourceCode["internal/bootstrap_node"];
let bootstrapFilename = "bootstrap_node.js";
if (!bootstrapSource) {
throw new FatalError("The node-cli mode is only compatible with Node 7.");
}
// We evaluate bootstrap script to get the bootstrap function.
let bootstrapFn = realm.$GlobalEnv.execute(bootstrapSource, bootstrapFilename, "");
if (!(bootstrapFn instanceof FunctionValue) || !bootstrapFn.$Call) {
throw new FatalError("The node bootstrap script should always yield a function.");
}
return bootstrapFn;
}

View File

@ -1,144 +0,0 @@
/**
* 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.
*/
/* @flow */
import invariant from "../../invariant.js";
import { FatalError } from "../../errors.js";
import { Realm } from "../../realm.js";
import { NumberValue, NativeFunctionValue, ObjectValue, StringValue } from "../../values/index.js";
import { getNodeBufferFromTypedArray } from "./utils.js";
import { Properties, To } from "../../singletons.js";
declare var process: any;
export default function(realm: Realm): ObjectValue {
let nativeBuffer = process.binding("buffer");
let nativeBufferPrototype = (require("buffer"): any).Buffer.prototype;
let intrinsicName = 'process.binding("buffer")';
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, intrinsicName);
// Buffer
let setupBufferJS = new NativeFunctionValue(
realm,
intrinsicName + ".setupBufferJS",
"setupBufferJS",
0,
(setupContext, setupArgs) => {
invariant(setupArgs.length === 2);
invariant(setupArgs[0] instanceof ObjectValue);
invariant(setupArgs[1] instanceof ObjectValue);
// TODO: Mutate the second argument by setting one of the properties to
// Buffer prototype just like the native implementation does.
let [proto] = setupArgs;
let simpleWrapperNames = [
"asciiSlice",
"base64Slice",
"latin1Slice",
"hexSlice",
"ucs2Slice",
"asciiWrite",
"base64Write",
"latin1Write",
"hexWrite",
"ucs2Write",
"utf8Write",
];
for (let name of simpleWrapperNames) {
let wrapper = new NativeFunctionValue(realm, "Buffer.prototype." + name, name, 0, (context, args) => {
throw new FatalError("TODO: " + name);
});
Properties.Set(realm, proto, name, wrapper, true);
}
// utf8Slice is used to read source code.
let utf8Slice = new NativeFunctionValue(realm, "Buffer.prototype.utf8Slice", "utf8Slice", 0, (context, args) => {
invariant(context instanceof ObjectValue);
let self = getNodeBufferFromTypedArray(realm, context);
let decodedArgs = args.map((arg, i) => To.ToInteger(realm, arg));
let utf8String = nativeBufferPrototype.utf8Slice.apply(self, decodedArgs);
return new StringValue(realm, utf8String);
});
Properties.Set(realm, proto, "utf8Slice", utf8Slice, true);
// copy has recently moved from the prototype to the instance upstream.
let copy = new NativeFunctionValue(realm, "Buffer.prototype.copy", "copy", 0, (context, args) => {
invariant(context instanceof ObjectValue);
let self = getNodeBufferFromTypedArray(realm, context);
let decodedArgs = args.map((arg, i) => {
if (i === 0) {
invariant(arg instanceof ObjectValue);
return getNodeBufferFromTypedArray(realm, arg);
} else {
return To.ToInteger(realm, arg);
}
});
let bytesCopied = nativeBufferPrototype.copy.apply(self, decodedArgs);
return new NumberValue(realm, bytesCopied);
});
Properties.Set(realm, proto, "copy", copy, true);
// TODO: Set up more methods on the prototype and bindingObject
return realm.intrinsics.undefined;
}
);
Properties.Set(realm, obj, "setupBufferJS", setupBufferJS, true);
let createFromString = new NativeFunctionValue(
realm,
intrinsicName + ".createFromString",
"createFromString",
0,
(context, args) => {
throw new FatalError("TODO");
}
);
Properties.Set(realm, obj, "createFromString", createFromString, true);
let simpleWrapperNames = [
"byteLengthUtf8",
"copy",
"compare",
"compareOffset",
"fill",
"indexOfBuffer",
"indexOfNumber",
"indexOfString",
"readDoubleBE",
"readDoubleLE",
"readFloatBE",
"readFloatLE",
"writeDoubleBE",
"writeDoubleLE",
"writeFloatBE",
"writeFloatLE",
"swap16",
"swap32",
"swap64",
];
for (let name of simpleWrapperNames) {
let wrapper = new NativeFunctionValue(realm, intrinsicName + "." + name, name, 0, (context, args) => {
throw new FatalError("TODO");
});
Properties.Set(realm, obj, name, wrapper, true);
}
Properties.Set(realm, obj, "kMaxLength", new NumberValue(realm, nativeBuffer.kMaxLength), true);
Properties.Set(realm, obj, "kStringMaxLength", new NumberValue(realm, nativeBuffer.kStringMaxLength), true);
return obj;
}

View File

@ -1,418 +0,0 @@
/**
* 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.
*/
/* @flow */
import invariant from "../../invariant.js";
import { Realm } from "../../realm.js";
import { AbruptCompletion, ThrowCompletion } from "../../completions.js";
import {
Value,
ConcreteValue,
BooleanValue,
EmptyValue,
NativeFunctionValue,
ObjectValue,
StringValue,
UndefinedValue,
} from "../../values/index.js";
import { Get, GetFunctionRealm } from "../../methods/index.js";
import { Properties, To } from "../../singletons.js";
import parse from "../../utils/parse.js";
import type { BabelNodeFile } from "@babel/types";
// 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.
import { transform as babelTransform } from "@babel/core";
// Hook for transpiling
function transform(code: string, filename: string): string {
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 = babelTransform(patchedCode, {
plugins: [
// Prepack doesn't support classes or destructuring yet.
"transform-es2015-classes",
"transform-es2015-destructuring",
"transform-es2015-parameters",
],
retainLines: true,
});
return transformedCode.code;
}
export default function(realm: Realm): ObjectValue {
let intrinsicName = 'process.binding("contextify")';
let obj = new 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 {
ast: BabelNodeFile;
constructor(ast: BabelNodeFile) {
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 = Get(realm, newTarget, "prototype");
if (!(proto instanceof ObjectValue)) {
realm = GetFunctionRealm(realm, newTarget);
proto = ContextifyScriptPrototype;
}
invariant(args[0] instanceof ConcreteValue);
let code = 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 ObjectValue(realm, proto, intrinsicConstructor);
if (cachedDataBuf.length) {
Properties.Set(realm, obj, "cachedDataRejected", realm.intrinsics.true, true);
}
if (produceCachedData) {
Properties.Set(realm, obj, "cachedDataProduced", realm.intrinsics.false, true);
}
let ast;
try {
// TODO: Somehow pass columnOffset to Babylon.
ast = parse(realm, transform(code, filename), filename, "script", 1 + lineOffset);
} catch (e) {
if (displayErrors && e instanceof ThrowCompletion) {
decorateErrorStack(e);
}
throw e;
}
// TODO: Pick up source map files and automatically fix up source locations.
(self: any).$InternalSlot = new ContextifyScriptInternal(ast);
return self;
}
let runInDebugContext = new NativeFunctionValue(
realm,
`${intrinsicName}.runInDebugContext`,
"runInDebugContext",
0,
runInDebugContextImpl
);
Properties.Set(realm, obj, "runInDebugContext", runInDebugContext, true);
let makeContext = new NativeFunctionValue(realm, `${intrinsicName}.makeContext`, "makeContext", 0, makeContextImpl);
Properties.Set(realm, obj, "makeContext", makeContext, true);
let isContext = new NativeFunctionValue(realm, `${intrinsicName}.isContext`, "isContext", 0, isContextImpl);
Properties.Set(realm, obj, "isContext", isContext, true);
let ContextifyScript = new NativeFunctionValue(
realm,
`${intrinsicName}.ContextifyScript`,
"ContextifyScript",
0,
ContextifyScriptConstructor,
true
);
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: AbruptCompletion): void {
let error = completion.value;
if (!(error instanceof ObjectValue)) {
return;
}
let errorData = error.$ErrorData;
if (!errorData) {
return;
}
let errorLocation = errorData.locationData;
if (!errorLocation || errorLocation.stackDecorated) {
return;
}
let stack = Get(realm, error, "stack");
if (!(stack instanceof 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}`;
Properties.Set(realm, error, "stack", new StringValue(realm, decoratedStack), false);
errorLocation.stackDecorated = true;
}
function getBreakOnSigintArg(options: Value): boolean {
if (options instanceof UndefinedValue || options instanceof StringValue) {
return false;
}
if (!(options instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object");
}
let value = Get(realm, options, "breakOnSigint");
invariant(value instanceof ConcreteValue);
return value instanceof BooleanValue && value.value;
}
function getTimeoutArg(options: Value): number {
if (options instanceof UndefinedValue || options instanceof StringValue) {
return -1;
}
if (!(options instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object");
}
let value = Get(realm, options, "timeout");
invariant(value instanceof ConcreteValue);
if (value instanceof UndefinedValue) {
return -1;
}
let timeout = To.ToInteger(realm, value);
if (timeout <= 0) {
throw realm.createErrorThrowCompletion(realm.intrinsics.RangeError, "timeout must be a positive number");
}
return timeout;
}
function getDisplayErrorsArg(options: Value): boolean {
if (options instanceof UndefinedValue || options instanceof StringValue) {
return true;
}
if (!(options instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object");
}
let value = Get(realm, options, "displayErrors");
invariant(value instanceof ConcreteValue);
if (value instanceof UndefinedValue) {
return true;
}
return To.ToBoolean(realm, value);
}
function getFilenameArg(options: Value): string {
const defaultFilename = "evalmachine.<anonymous>";
if (options instanceof UndefinedValue) {
return defaultFilename;
}
if (options instanceof StringValue) {
return options.value;
}
if (!(options instanceof ObjectValue)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "options must be an object");
}
let value = Get(realm, options, "filename");
invariant(value instanceof ConcreteValue);
if (value instanceof UndefinedValue) {
return defaultFilename;
}
return To.ToString(realm, value);
}
function getCachedData(options: Value): Uint8Array {
if (!(options instanceof ObjectValue)) {
return new Uint8Array(0);
}
let value = Get(realm, options, "cachedData");
invariant(value instanceof ConcreteValue);
if (value instanceof UndefinedValue) {
return new Uint8Array(0);
}
if (
!(value instanceof ObjectValue) ||
!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: Value): boolean {
if (!(options instanceof ObjectValue)) {
return false;
}
let value = Get(realm, options, "produceCachedData");
invariant(value instanceof ConcreteValue);
return value instanceof BooleanValue && value.value;
}
function getLineOffsetArg(options: Value): number {
const defaultLineOffset = 0;
if (!(options instanceof ObjectValue)) {
return defaultLineOffset;
}
let value = Get(realm, options, "lineOffset");
invariant(value instanceof ConcreteValue);
return value instanceof UndefinedValue ? defaultLineOffset : To.ToInteger(realm, value);
}
function getColumnOffsetArg(options: Value): number {
const defaultColumnOffset = 0;
if (!(options instanceof ObjectValue)) {
return defaultColumnOffset;
}
let value = Get(realm, options, "columnOffset");
invariant(value instanceof ConcreteValue);
return value instanceof UndefinedValue ? defaultColumnOffset : To.ToInteger(realm, value);
}
function evalMachine(self: Value, timeout: number, displayErrors: boolean, breakOnSigint: boolean): Value {
if (!(self instanceof ObjectValue) || !((self: any).$InternalSlot instanceof ContextifyScriptInternal)) {
throw realm.createErrorThrowCompletion(
realm.intrinsics.Error,
"Script methods can only be called on script instances."
);
}
let script = ((self: any).$InternalSlot: ContextifyScriptInternal);
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);
invariant(context.lexicalEnvironment === realm.$GlobalEnv);
realm.onDestroyScope(context.lexicalEnvironment);
}
invariant(realm.getRunningContext() === previousContext);
previousContext.resume();
if (result instanceof EmptyValue) {
return realm.intrinsics.undefined;
} else if (result instanceof Value) {
return result;
} else {
invariant(result instanceof AbruptCompletion);
if (displayErrors) {
decorateErrorStack(result);
}
throw result;
}
}
let ContextifyScriptPrototype = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
`${intrinsicName}.ContextifyScript.prototype`
);
ContextifyScriptPrototype.defineNativeMethod("runInContext", 2, runInContext);
ContextifyScriptPrototype.defineNativeMethod("runInThisContext", 1, runInThisContext);
Properties.DefinePropertyOrThrow(realm, ContextifyScript, "prototype", {
value: ContextifyScriptPrototype,
writable: true,
enumerable: false,
configurable: false,
});
Properties.DefinePropertyOrThrow(realm, ContextifyScriptPrototype, "constructor", {
value: ContextifyScript,
writable: true,
enumerable: false,
configurable: true,
});
return obj;
}

View File

@ -1,99 +0,0 @@
/**
* 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.
*/
/* @flow */
import invariant from "../../invariant.js";
import type { Realm } from "../../realm.js";
import { AbstractValue, NumberValue, ObjectValue, StringValue } from "../../values/index.js";
import { ValuesDomain } from "../../domains/index.js";
import buildExpressionTemplate from "../../utils/builder.js";
import { getNodeBufferFromTypedArray } from "./utils.js";
import { Properties, To } from "../../singletons.js";
declare var process: any;
export default function(realm: Realm): ObjectValue {
let intrinsicName = 'process.binding("fs")';
let nativeFS = process.binding("fs");
// fs
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, intrinsicName);
obj.defineNativeMethod("FSInitialize", 0, (context, args) => {
// TODO: Implement the native implementation.
return realm.intrinsics.undefined;
});
obj.defineNativeMethod("internalModuleStat", 0, (context, args) => {
const fileName = To.ToString(realm, args[0]);
return new NumberValue(realm, nativeFS.internalModuleStat(fileName));
});
obj.defineNativeMethod("lstat", 0, (context, args) => {
const path = To.ToString(realm, args[0]);
invariant(args[1] instanceof ObjectValue);
const buffer = getNodeBufferFromTypedArray(realm, args[1]);
const float64buffer = new Float64Array(buffer.buffer);
nativeFS.lstat(path, float64buffer);
return args[1];
});
obj.defineNativeMethod("fstat", 0, (context, args) => {
const fd = To.ToNumber(realm, args[0]);
invariant(args[1] instanceof ObjectValue);
const buffer = getNodeBufferFromTypedArray(realm, args[1]);
const float64buffer = new Float64Array(buffer.buffer);
nativeFS.fstat(fd, float64buffer);
return args[1];
});
obj.defineNativeMethod("open", 0, (context, args) => {
const path = To.ToString(realm, args[0]);
const flags = To.ToNumber(realm, args[1]);
const mode = To.ToNumber(realm, args[2]);
const fd = nativeFS.open(path, flags, mode);
return new NumberValue(realm, fd);
});
obj.defineNativeMethod("close", 0, (context, args) => {
const fd = To.ToNumber(realm, args[0]);
nativeFS.close(fd);
return realm.intrinsics.undefined;
});
obj.defineNativeMethod("read", 0, (context, args) => {
const fd = To.ToNumber(realm, args[0]);
invariant(args[1] instanceof ObjectValue);
const buffer = getNodeBufferFromTypedArray(realm, args[1]);
const offset = To.ToNumber(realm, args[2]);
const length = To.ToNumber(realm, args[3]);
const position = args[4] === realm.intrinsics.undefined ? undefined : To.ToNumber(realm, args[4]);
const bytesRead = nativeFS.read(fd, buffer, offset, length, position);
return new NumberValue(realm, bytesRead);
});
obj.defineNativeMethod("internalModuleReadFile", 0, (context, args) => {
const path = To.ToString(realm, args[0]);
const result = nativeFS.internalModuleReadFile(path);
if (result === undefined) {
return realm.intrinsics.undefined;
}
return new StringValue(realm, result);
});
let FSReqWrapTemplateSrc = `${intrinsicName}.FSReqWrap`;
let FSReqWrapTemplate = buildExpressionTemplate(FSReqWrapTemplateSrc);
let val = AbstractValue.createFromTemplate(realm, FSReqWrapTemplate, ObjectValue, [], FSReqWrapTemplateSrc);
val.values = new ValuesDomain(new Set([new ObjectValue(realm)]));
val.intrinsicName = FSReqWrapTemplateSrc;
Properties.DefinePropertyOrThrow(realm, obj, "FSReqWrap", {
value: val,
writable: true,
configurable: true,
enumerable: true,
});
// TODO: Implement more of the native methods here. Ideally all of them should
// just be automatically proxied.
return obj;
}

View File

@ -1,649 +0,0 @@
/**
* 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.
*/
/* @flow */
import invariant from "../../invariant.js";
import type { Realm } from "../../realm.js";
import {
AbstractObjectValue,
AbstractValue,
ArrayValue,
BooleanValue,
ConcreteValue,
FunctionValue,
NativeFunctionValue,
NumberValue,
ObjectValue,
StringValue,
} from "../../values/index.js";
import { Get } from "../../methods/index.js";
import { ValuesDomain } from "../../domains/index.js";
import { Properties, To } from "../../singletons.js";
import buildExpressionTemplate from "../../utils/builder.js";
import initializeBuffer from "./buffer.js";
import initializeContextify from "./contextify.js";
import initializeFS from "./fs.js";
import { copyProperty, createDeepIntrinsic } from "./utils.js";
declare var process: any;
function initializeTimerWrap(realm) {
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.binding('timer_wrap')");
let constructor = new NativeFunctionValue(
realm,
"process.binding('timer_wrap').Timer",
"Timer",
0,
(context, args) => {
return realm.intrinsics.undefined;
}
);
Properties.OrdinaryDefineOwnProperty(realm, obj, "Timer", {
value: constructor,
writable: true,
enumerable: true,
configurable: true,
});
// TODO: Implement the rest of this protocol as needed.
return obj;
}
function initializeTTYWrap(realm) {
let nativeTTYWrap = process.binding("tty_wrap");
// let nativeTTY = nativeTTYWrap.TTY;
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.binding('tty_wrap')");
let constructor = new NativeFunctionValue(
realm,
"process.binding('tty_wrap').TTY",
"TTY",
0,
(context, args, argCount, NewTarget) => {
invariant(args[0] instanceof ConcreteValue);
let fd = To.ToInteger(realm, args[0]);
invariant(args[1] instanceof ConcreteValue);
let value = To.ToBoolean(realm, args[1]);
invariant(NewTarget, "TTY must be called as a constructor.");
let proto = Get(realm, NewTarget, new StringValue(realm, "prototype"));
if (!(proto instanceof ObjectValue)) {
proto = TTYPrototype;
}
// TODO: Store nativeTTY in an internal slot so that it can be used if this
// object gets passed to another native call.
return new ObjectValue(realm, proto, `new (process.binding('tty_wrap').TTY)(${fd}, ${value.toString()})`);
}
);
Properties.OrdinaryDefineOwnProperty(realm, obj, "TTY", {
value: constructor,
writable: true,
enumerable: true,
configurable: true,
});
let TTYPrototype = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"process.binding('tty_wrap').TTY.prototype"
);
TTYPrototype.defineNativeMethod("setBlocking", 0, (context, args) => {
return realm.intrinsics.undefined;
});
TTYPrototype.defineNativeMethod("getWindowSize", 0, (context, args) => {
return realm.intrinsics.undefined;
});
TTYPrototype.defineNativeMethod("writeUtf8String", 0, (context, args) => {
// TODO: Store this as a side-effect. When we do that, we need the first arg
// to be passed along to that side-effect.
// let req = args[0];
let content = args[1];
invariant(content instanceof StringValue);
return realm.intrinsics.undefined;
});
Properties.DefinePropertyOrThrow(realm, constructor, "prototype", {
value: TTYPrototype,
writable: true,
enumerable: false,
configurable: false,
});
obj.defineNativeMethod("guessHandleType", 0, (context, args) => {
let fd = To.ToInteger(realm, args[0]);
return new StringValue(realm, nativeTTYWrap.guessHandleType(fd));
// TODO: Make this abstract so that changing the pipe at runtime is
// possible. Currently this causes an introspection error.
// let types = new TypesDomain(StringValue);
// let values = new ValuesDomain(new Set([
// new StringValue(realm, "TCP"),
// new StringValue(realm, "TTY"),
// new StringValue(realm, "UDP"),
// new StringValue(realm, "FILE"),
// new StringValue(realm, "PIPE"),
// new StringValue(realm, "UNKNOWN")
// ]));
// let buildNode = buildExpressionTemplate(
// `(process.binding('tty_wrap').guessHandleType(${fd}))`
// )(this.realm.preludeGenerator);
// return realm.createAbstract(types, values, [], buildNode, undefined, `(process.binding('tty_wrap').guessHandleType(${fd}))`);
});
obj.defineNativeMethod("isTTY", 0, (context, args) => {
let fd = To.ToInteger(realm, args[0]);
const isTTYtemplateSrc = `(process.binding('tty_wrap').isTTY(${fd}))`;
const isTTYtemplate = buildExpressionTemplate(isTTYtemplateSrc);
let val = AbstractValue.createFromTemplate(realm, isTTYtemplate, BooleanValue, [], isTTYtemplateSrc);
val.intrinsicName = isTTYtemplateSrc;
return val;
});
// TODO: Implement the rest of this protocol.
return obj;
}
function initializeSignalWrap(realm) {
// TODO: Implement more of this protocol. When doing so, we'll likely need to
// forward it to the native implementation.
// let nativeSignalWrap = process.binding("signal_wrap");
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.binding('signal_wrap')");
let constructor = new NativeFunctionValue(
realm,
"process.binding('signal_wrap').Signal",
"Signal",
0,
(context, args) => {
return realm.intrinsics.undefined;
}
);
Properties.OrdinaryDefineOwnProperty(realm, obj, "Signal", {
value: constructor,
writable: true,
enumerable: true,
configurable: true,
});
let SignalPrototype = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"process.binding('signal_wrap').Signal.prototype"
);
SignalPrototype.defineNativeMethod("unref", 0, (context, args) => {
// TODO: Track the side-effect of this.
return realm.intrinsics.undefined;
});
SignalPrototype.defineNativeMethod("start", 0, (context, args) => {
// TODO: Track the side-effect of this.
return realm.intrinsics.undefined;
});
SignalPrototype.defineNativeMethod("close", 0, (context, args) => {
// TODO: Track the side-effect of this.
return realm.intrinsics.undefined;
});
Properties.DefinePropertyOrThrow(realm, constructor, "prototype", {
value: SignalPrototype,
writable: true,
enumerable: false,
configurable: false,
});
// TODO
return obj;
}
function initializeStreamWrap(realm) {
// let nativeStreamWrap = process.binding("stream_wrap");
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.binding('stream_wrap')");
let constructor = new NativeFunctionValue(
realm,
"process.binding('stream_wrap').WriteWrap",
"WriteWrap",
0,
(context, args) => {
return realm.intrinsics.undefined;
}
);
Properties.OrdinaryDefineOwnProperty(realm, obj, "WriteWrap", {
value: constructor,
writable: true,
enumerable: true,
configurable: true,
});
let WriteWrapPrototype = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"process.binding('stream_wrap').WriteWrap.prototype"
);
WriteWrapPrototype.defineNativeMethod("unref", 0, (context, args) => {
// TODO: Track the side-effect of this.
return realm.intrinsics.undefined;
});
Properties.DefinePropertyOrThrow(realm, constructor, "prototype", {
value: WriteWrapPrototype,
writable: true,
enumerable: false,
configurable: false,
});
let ShutdownWrap = createAbstractValue(realm, FunctionValue, "process.binding('stream_wrap').ShutdownWrap");
Properties.DefinePropertyOrThrow(realm, obj, "ShutdownWrap", {
value: ShutdownWrap,
writable: true,
configurable: true,
enumerable: true,
});
// TODO
return obj;
}
function initializeFSEvent(realm) {
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.binding('fs_event_wrap')");
let FSEvent = createAbstractValue(realm, FunctionValue, "process.binding('fs_event_wrap').FSEvent");
Properties.DefinePropertyOrThrow(realm, obj, "FSEvent", {
value: FSEvent,
writable: true,
configurable: true,
enumerable: true,
});
// TODO
return obj;
}
function initializeURL(realm) {
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
// TODO
return obj;
}
function initializeUtil(realm) {
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, 'process.binding("util")');
obj.defineNativeMethod("isUint8Array", 0, (context, args) => {
let arr = args[0];
if (arr instanceof ObjectValue && arr.$TypedArrayName === "Uint8Array") {
return realm.intrinsics.true;
}
return realm.intrinsics.false;
});
copyProperty(
realm,
process.binding("util"),
obj,
"pushValToArrayMax",
new NumberValue(realm, process.binding("util").pushValToArrayMax, 'process.binding("util").pushValToArrayMax')
);
// TODO
return obj;
}
function createAbstractValue(realm, type, intrinsicName): AbstractObjectValue {
let template = buildExpressionTemplate(intrinsicName);
let val = AbstractValue.createFromTemplate(realm, template, ObjectValue, [], intrinsicName);
val.values = new ValuesDomain(new Set([new ObjectValue(realm)]));
val.intrinsicName = intrinsicName;
return (val: any);
}
function createIntrinsicArrayValue(realm, intrinsicName) {
// Like ArrayCreate but accepts an intrinsic name.
let obj = new ArrayValue(realm, intrinsicName);
obj.setExtensible(true);
Properties.OrdinaryDefineOwnProperty(realm, obj, "length", {
value: realm.intrinsics.zero,
writable: true,
enumerable: false,
configurable: false,
});
return obj;
}
function reverseConfigJSON(config) {
// Hack to restore the gyp config format
let json = JSON.stringify(process.config).replace(/"/g, "'");
return "\n" + json;
}
export default function(realm: Realm, processArgv: Array<string>): ObjectValue {
if (!realm.useAbstractInterpretation) {
invariant(false, "Realm is not enabled for Abstract Interpretation");
}
// TODO: This causes a dependency on the native `process` which doesn't
// exist in all environments such as the webpack version.
// Constant bindings
// TODO: Implement icu module so that we can let hasIntl be true.
let configOverride = { ...process.binding("config"), hasIntl: false };
// By the time we run the host has already deleted natives.config so we have
// to restore it.
let nativeOverride = { ...process.binding("natives"), config: reverseConfigJSON(process.config) };
let config = createDeepIntrinsic(realm, configOverride, 'process.binding("config")');
let constants = createDeepIntrinsic(realm, process.binding("constants"), 'process.binding("constants")');
let natives = createDeepIntrinsic(realm, nativeOverride, 'process.binding("natives")');
// Built-in native bindings
let contextify = initializeContextify(realm);
let fs = initializeFS(realm);
let fsEvent = initializeFSEvent(realm);
let url = initializeURL(realm);
let timerWrap = initializeTimerWrap(realm);
let ttyWrap = initializeTTYWrap(realm);
let signalWrap = initializeSignalWrap(realm);
let streamWrap = initializeStreamWrap(realm);
let caresWrap = createAbstractValue(realm, ObjectValue, 'process.binding("cares_wrap")');
let tcpWrap = createAbstractValue(realm, ObjectValue, 'process.binding("tcp_wrap")');
tcpWrap.makeSimple();
let pipeWrap = createAbstractValue(realm, ObjectValue, 'process.binding("pipe_wrap")');
pipeWrap.makeSimple();
let uv = createAbstractValue(realm, ObjectValue, 'process.binding("uv")');
let buffer = initializeBuffer(realm);
let util = initializeUtil(realm);
let os = createAbstractValue(realm, ObjectValue, 'process.binding("os")');
os.makeSimple();
// List of loaded native modules
let moduleLoadList = createIntrinsicArrayValue(realm, "process.moduleLoadList");
// The process object
let obj = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process");
obj.defineNativeMethod("binding", 1, (context, args) => {
let arg0 = args.length < 1 ? realm.intrinsics.undefined : args[0];
let module = To.ToString(realm, arg0);
// TODO: Add the module to the moduleLoadList but don't track that
// as a side-effect.
switch (module) {
// Constants
case "config":
return config;
case "constants":
return constants;
case "natives":
return natives;
// Built-in bindings
case "contextify":
return contextify;
case "fs":
return fs;
case "fs_event_wrap":
return fsEvent;
case "url":
return url;
case "uv":
return uv;
case "buffer":
return buffer;
case "util":
return util;
case "os":
return os;
case "timer_wrap":
return timerWrap;
case "tty_wrap":
return ttyWrap;
case "cares_wrap":
return caresWrap;
case "tcp_wrap":
return tcpWrap;
case "pipe_wrap":
return pipeWrap;
case "stream_wrap":
return streamWrap;
case "signal_wrap":
return signalWrap;
default:
throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, `No such module: ${module}`);
}
});
copyProperty(realm, process, obj, "moduleLoadList", moduleLoadList);
// Constants on the process
let constantNames = [
"version",
"versions",
"_promiseRejectEvent",
"arch",
"platform",
"release",
"features",
"_needImmediateCallback",
];
for (let name of constantNames) {
let value = createDeepIntrinsic(realm, process[name], "process." + name);
copyProperty(realm, process, obj, name, value);
}
// process._events is a mutable object with null prototype.
let _events = new ObjectValue(realm, realm.intrinsics.null, "process._events");
copyProperty(realm, process, obj, "_events", _events);
// TODO: When abstract numbers in string templates is implemented, turn this
// back into an abstract value.
// let pid = createAbstractValue(realm, NumberValue, "process.pid");
let pid = new NumberValue(realm, 0, "process.pid");
copyProperty(realm, process, obj, "pid", pid);
let debugPort = createAbstractValue(realm, NumberValue, "process.debugPort");
copyProperty(realm, process, obj, "debugPort", debugPort);
let title = createAbstractValue(realm, StringValue, "process.title");
copyProperty(realm, process, obj, "title", title);
// process.execArgv should probably be passed as an argument to the compile
// step rather than letting arbitrary options be passed to the program.
// For now I'll just hard code it as empty array.
// TODO: Allow execArgv to be passed as a compiler option.
let execArgv = createIntrinsicArrayValue(realm, "process.execArgv");
copyProperty(realm, process, obj, "execArgv", execArgv);
let cwd = new NativeFunctionValue(realm, "process.cwd", "cwd", 0, (context, args) => {
return new StringValue(realm, process.cwd(), "process.cwd()");
});
copyProperty(realm, process, obj, "cwd", cwd);
// These properties all depend on options being defined in "execArgv" but
// since we hard coded it, none of this will be added.
// "_eval" : string
// "_print_eval" : boolean
// "_syntax_check_only" : boolean
// "_forceRepl" : boolean
// "noDeprecation" : boolean
// "noProcessWarnings" : boolean
// "traceProcessWarnings" : boolean
// "throwDeprecation" : boolean
// "_noBrowserGlobals" : boolean
// "profProcess" : boolean
// "traceDeprecation" : boolean
// "_debugWaitConnect" : boolean
// "_preload_modules" gets looped over so it needs to be known but typically
// we don't need to do this so we can just leave it not defined.
// Side-effectful Methods
let methodNames = [
"_startProfilerIdleNotifier",
"_stopProfilerIdleNotifier",
"_getActiveRequests",
"_getActiveHandles",
"reallyExit",
"abort",
"chdir",
"umask",
// Start Posix only
"getuid",
"geteuid",
"setuid",
"seteuid",
"setgid",
"setegid",
"getgid",
"getegid",
"getgroups",
"setgroups",
"initgroups",
// End Posix only
"_kill",
"_debugProcess",
"_debugPause",
"_debugEnd",
"hrtime",
"cpuUsage",
"dlopen",
"uptime",
"memoryUsage",
"_linkedBinding",
"_setupNextTick",
"_setupPromises",
"_setupDomainUse",
];
for (let name of methodNames) {
let abstractMethod = createAbstractValue(realm, FunctionValue, "process." + name);
copyProperty(realm, process, obj, name, abstractMethod);
}
let argv0 = new StringValue(realm, process.argv0, "process.argv0");
Properties.DefinePropertyOrThrow(realm, obj, "argv0", {
value: argv0,
writable: false,
configurable: true,
enumerable: true,
});
let argv = createAbstractValue(realm, ObjectValue, "process.argv");
Properties.DefinePropertyOrThrow(realm, argv, "0", {
value: argv0,
writable: true,
configurable: true,
enumerable: true,
});
Properties.DefinePropertyOrThrow(realm, argv, "1", {
value: new StringValue(realm, processArgv[1]),
writable: true,
configurable: true,
enumerable: true,
});
Properties.DefinePropertyOrThrow(realm, argv, "indexOf", {
value: new NativeFunctionValue(realm, "process.argv.indexOf", "indexOf", 0, (context, args) => {
return realm.intrinsics.false;
}),
writable: true,
configurable: true,
enumerable: true,
});
argv.makeSimple();
copyProperty(realm, process, obj, "argv", argv);
let execPath = new StringValue(realm, process.execPath, "process.execPath");
copyProperty(realm, process, obj, "execPath", execPath);
let env = new ObjectValue(realm, realm.intrinsics.ObjectPrototype, "process.env");
// TODO: This abstract value doesn't work with a conditional for some reason.
Properties.DefinePropertyOrThrow(realm, env, "NODE_NO_WARNINGS", {
value: new StringValue(realm, "0", "process.env.NODE_NO_WARNINGS"),
writable: true,
configurable: true,
enumerable: true,
});
// Uncomment this to debug the module resolution system.
// DefinePropertyOrThrow(realm, env, "NODE_DEBUG", {
// value: new StringValue(
// realm, "module", "process.env.NODE_DEBUG"
// ),
// writable: true,
// configurable: true,
// enumerable: true,
// });
env.makeSimple();
copyProperty(realm, process, obj, "env", env);
// This method just gets passed a value from the initialization code and
// then deletes itself.
// TODO: The generated code needs to either always invoke this (make it
// abstract) or, if we assume it has been done, it doesn't need to delete it.
obj.defineNativeMethod("_setupProcessObject", 1, (self, [pushValueToArray]) => {
Properties.OrdinaryDelete(realm, obj, "_setupProcessObject");
return realm.intrinsics.undefined;
});
// This method injects a generic global promise reject callback. In real
// environment we'd want to call this at rejections but we can safely skip it.
obj.defineNativeMethod("_setupPromises", 1, (self, [promiseRejectCallback]) => {
Properties.OrdinaryDelete(realm, obj, "_setupPromises");
return realm.intrinsics.undefined;
});
// TODO: Support Promises. Set up a micro task runner and invoke the
// tickCallback as needed.
obj.defineNativeMethod("_setupNextTick", 1, (self, [tickCallback, runMicrotasks]) => {
Properties.OrdinaryDelete(realm, obj, "_setupNextTick");
let runMicrotasksCallback = new NativeFunctionValue(
realm,
"(function() { throw new Error('TODO runMicrotasks not reachable') })",
"runMicrotasks",
0,
(context, args) => {
// TODO: Implement Promises and micro tasks.
return realm.intrinsics.undefined;
}
);
Properties.OrdinaryDefineOwnProperty(realm, runMicrotasks, "runMicrotasks", {
value: runMicrotasksCallback,
writable: true,
enumerable: true,
configurable: true,
});
let tickInfo = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
"(function() { throw new Error('TODO tickInfo is not reachable in the host environment') })"
);
Properties.OrdinaryDefineOwnProperty(realm, tickInfo, "0", {
value: realm.intrinsics.zero,
writable: true,
enumerable: true,
configurable: true,
});
Properties.OrdinaryDefineOwnProperty(realm, tickInfo, "1", {
value: realm.intrinsics.zero,
writable: true,
enumerable: true,
configurable: true,
});
return tickInfo;
});
return obj;
}

View File

@ -1,92 +0,0 @@
/**
* 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.
*/
/* @flow */
import invariant from "../../invariant.js";
import { FatalError } from "../../errors.js";
import type { Realm } from "../../realm.js";
import { type Value, BooleanValue, ObjectValue, NumberValue, StringValue } from "../../values/index.js";
import { Properties } from "../../singletons.js";
export function getNodeBufferFromTypedArray(realm: Realm, value: ObjectValue): Uint8Array {
let buffer = value.$ViewedArrayBuffer;
invariant(buffer instanceof ObjectValue && buffer.$ArrayBufferData);
return buffer.$ArrayBufferData;
}
// Takes a value from the host realm and create it into a Prepack Realm.
// TODO: Move this to a bigger general purpose proxy between the environments.
// See issue #644 for more details.
export function createDeepIntrinsic(realm: Realm, value: mixed, intrinsicName: string): Value {
switch (typeof value) {
case "undefined":
return realm.intrinsics.undefined;
case "boolean":
return new BooleanValue(realm, value, intrinsicName);
case "number":
return new NumberValue(realm, value, intrinsicName);
case "string":
return new StringValue(realm, value, intrinsicName);
// $FlowFixMe flow doesn't understand symbols.
case "symbol":
throw new FatalError("Symbol cannot be safely cloned.");
case "function":
throw new FatalError("Functions could be supported but are not yet.");
case "object": {
if (value === null) {
return realm.intrinsics.null;
}
if (Array.isArray(value)) {
throw new FatalError("Arrays are not supported yet.");
}
let prototype = Object.getPrototypeOf(value);
if (prototype !== (Object: any).prototype) {
throw new FatalError(
"Only simple objects are supported for now. Got: " +
((typeof (prototype: any).constructor === "function" && prototype.constructor.name) ||
Object.prototype.toString.call(prototype))
);
}
let obj = new ObjectValue(
realm,
realm.intrinsics.ObjectPrototype,
intrinsicName // We use the intrinsic name for Objects to preserve their referential equality
);
let names = Object.getOwnPropertyNames(value);
for (let name of names) {
// We intentionally invoke the getter on value[name] which resolves any
// lazy getters.
let newValue = createDeepIntrinsic(realm, value[name], intrinsicName + "." + name);
copyProperty(realm, value, obj, name, newValue);
}
return obj;
}
default:
invariant(false);
}
}
// Define a value with the same descriptor settings as the original object.
export function copyProperty(realm: Realm, originalObject: {}, realmObject: ObjectValue, name: string, value: Value) {
let desc = Object.getOwnPropertyDescriptor(originalObject, name);
if (!desc) {
return;
}
if (desc.get || desc.set) {
throw new FatalError("Getter/setters are not supported because functions are not supported yet.");
}
let newDesc = {
value: value,
writable: !!desc.writable,
configurable: !!desc.configurable,
enumerable: !!desc.enumerable,
};
Properties.DefinePropertyOrThrow(realm, realmObject, name, newDesc);
}

View File

@ -11,23 +11,8 @@
import type { ErrorHandler } from "./errors.js";
export type Compatibility =
| "browser"
| "jsc-600-1-4-17"
| "mobile"
| "node-source-maps"
| "node-cli"
| "fb-www"
| "node-react";
export const CompatibilityValues = [
"browser",
"jsc-600-1-4-17",
"mobile",
"node-source-maps",
"node-cli",
"fb-www",
"node-react",
];
export type Compatibility = "browser" | "jsc-600-1-4-17" | "mobile" | "node-source-maps" | "fb-www" | "node-react";
export const CompatibilityValues = ["browser", "jsc-600-1-4-17", "mobile", "node-source-maps", "fb-www", "node-react"];
export type InvariantModeTypes =
| "throw"

View File

@ -1,117 +0,0 @@
/**
* 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.
*/
/* @flow */
/* API functions for running Prepack on code that expects to run on Node */
import invariant from "./invariant.js";
import { ExecutionContext } from "./realm.js";
import Serializer from "./serializer/index.js";
import { SerializerStatistics } from "./serializer/statistics.js";
import { Completion } from "./completions.js";
import { Value } from "./values";
import construct_realm from "./construct_realm.js";
import initializeGlobals from "./globals.js";
import { getRealmOptions, getSerializerOptions } from "./prepack-options";
import { FatalError } from "./errors.js";
import initializeBootstrap from "./intrinsics/node/bootstrap.js";
import initializeProcess from "./intrinsics/node/process.js";
import type { PrepackOptions } from "./prepack-options";
import { defaultOptions } from "./options";
import type { SourceMap } from "./types.js";
declare var process: any;
export function prepackNodeCLI(
filename: string,
options: PrepackOptions = defaultOptions,
callback: (any, ?{ code: string, map?: SourceMap }) => void
): void {
let serialized;
try {
serialized = prepackNodeCLISync(filename, options);
} catch (err) {
callback(err);
return;
}
callback(null, serialized);
}
export function prepackNodeCLISync(filename: string, options: PrepackOptions = defaultOptions) {
if (process.version !== "v7.9.0") {
console.warn(
`Prepack's node-cli mode currently only works on Node v7.9.0.\n` +
`You are running version ${process.version} which will likely fail.`
);
}
let realm = construct_realm(getRealmOptions(options), undefined, new SerializerStatistics());
initializeGlobals(realm);
let processObj = initializeProcess(realm, ["node", filename]);
let bootstrapFn = initializeBootstrap(realm);
let serializer = new Serializer(realm, getSerializerOptions(options));
let context = new ExecutionContext();
context.lexicalEnvironment = realm.$GlobalEnv;
context.variableEnvironment = realm.$GlobalEnv;
context.realm = realm;
realm.pushContext(context);
let res;
try {
if (bootstrapFn.$Call) {
res = bootstrapFn.$Call(realm.intrinsics.null, [processObj]);
}
} catch (err) {
if (err instanceof Completion) {
res = err;
} else if (err instanceof Error) {
throw err;
} else {
throw new FatalError(err);
}
} finally {
realm.popContext(context);
}
if (res instanceof Completion) {
context = new ExecutionContext();
realm.pushContext(context);
try {
serializer.logger.logCompletion(res);
} finally {
realm.popContext(context);
realm.onDestroyScope(realm.$GlobalEnv);
}
}
// Hack: Turn these objects generated by the bootstrap script into
// intrinsics that exist in a preinitialized environment. This ensures
// that we don't end up with duplicates of these. This won't work in an
// uninitialized environment.
let nextTick = realm.$GlobalEnv.execute("process.nextTick", "", "");
invariant(nextTick instanceof Value);
nextTick.intrinsicName = "process.nextTick";
let tickCallback = realm.$GlobalEnv.execute("process._tickCallback", "", "");
invariant(tickCallback instanceof Value);
tickCallback.intrinsicName = "process._tickCallback";
let tickDomainCallback = realm.$GlobalEnv.execute("process._tickDomainCallback", "", "");
invariant(tickDomainCallback instanceof Value);
tickDomainCallback.intrinsicName = "process._tickDomainCallback";
// Serialize
let sources = [{ filePath: "", fileContents: "" }];
let serialized = serializer.init(sources, options.sourceMaps);
if (!serialized) {
throw new FatalError("serializer failed");
}
return serialized;
}

View File

@ -15,7 +15,6 @@
import { defaultOptions } from "./options";
import { FatalError } from "./errors.js";
import { type PrepackOptions } from "./prepack-options";
import { prepackNodeCLI, prepackNodeCLISync } from "./prepack-node-environment.js";
import { prepackSources } from "./prepack-standalone.js";
import { type SourceMap } from "./types.js";
import { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
@ -25,7 +24,6 @@ import { SerializerStatistics } from "./serializer/statistics.js";
import fs from "fs";
export * from "./prepack-node-environment";
export * from "./prepack-standalone";
function createStatistics(options: PrepackOptions) {
@ -87,10 +85,6 @@ export function prepackFile(
callback: (any, ?{ code: string, map?: SourceMap }) => void,
fileErrorHandler?: (err: ?Error) => void
): void {
if (options.compatibility === "node-cli") {
prepackNodeCLI(filename, options, callback);
return;
}
let sourceMapFilename =
options.inputSourceMapFilename !== undefined ? options.inputSourceMapFilename : filename + ".map";
fs.readFile(filename, "utf8", function(fileErr, code) {
@ -122,13 +116,6 @@ export function prepackFile(
}
export function prepackFileSync(filenames: Array<string>, options: PrepackOptions = defaultOptions): SerializedResult {
if (options.compatibility === "node-cli") {
if (filenames.length !== 1) {
console.error(`Does not support multiple file prepack in node-cli mode.`);
process.exit(1);
}
return prepackNodeCLISync(filenames[0], options);
}
const sourceFiles = filenames.map(filename => {
let code = fs.readFileSync(filename, "utf8");
let sourceMap = "";

View File

@ -2389,7 +2389,7 @@ export class ResidualHeapSerializer {
}
if (this._shouldBeWrapped(body)) {
let globalExpression = this.realm.isCompatibleWith("node-cli") ? t.identifier("global") : t.thisExpression();
let globalExpression = t.thisExpression();
let functionExpression = t.functionExpression(null, [], t.blockStatement(body, globalDirectives));
let callExpression = this.preludeGenerator.usesThis

View File

@ -1,24 +0,0 @@
var fs = require("fs");
var greetingPath = require.resolve("./Greeting.txt");
var greetingJSON = require("./Greeting.json");
// The residual program after initialization.
__residual(
"boolean",
function(fs, greetingJSON, greetingPath, console, JSON) {
var greetingText = fs.readFileSync(greetingPath, "utf8");
console.log(`${greetingJSON.greeting} ${greetingText} world!`);
// Currently, we're required to have a return value even though
// we're not going to use it.
return false;
},
fs,
greetingJSON,
greetingPath,
console,
JSON
);

View File

@ -1,7 +0,0 @@
let greeting = "Hello";
let now = Date.now();
let future = +new Date(2000, 1, 1);
if (now > future) {
greeting = "Hey";
}
module.exports = greeting;

View File

@ -1,3 +0,0 @@
{
"greeting": "Hey"
}

View File

@ -1 +0,0 @@
Hello

View File

@ -1,15 +0,0 @@
var greeting = require("./Greeting");
// The residual program after initialization.
__residual(
"boolean",
function(greeting, console) {
console.log(greeting + " world!");
// Currently, we're required to have a return value even though
// we're not going to use it.
return false;
},
greeting,
console
);

View File

@ -40,7 +40,7 @@ var optionsConfig = [
{
type: "choice",
name: "compatibility",
choices: ["browser", "jsc-600-1-4-17", "node-source-maps", "node-cli", "node-react"],
choices: ["browser", "jsc-600-1-4-17", "node-source-maps", "node-react"],
defaultVal: "node-react",
description: "The target environment for Prepack"
},