mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
src: add support for TLA
PR-URL: https://github.com/nodejs/node/pull/30370 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com>
This commit is contained in:
@@ -889,6 +889,11 @@ provided.
|
||||
Encoding provided to `TextDecoder()` API was not one of the
|
||||
[WHATWG Supported Encodings][].
|
||||
|
||||
<a id="ERR_EVAL_ESM_CANNOT_PRINT"></a>
|
||||
### `ERR_EVAL_ESM_CANNOT_PRINT`
|
||||
|
||||
`--print` cannot be used with ESM input.
|
||||
|
||||
<a id="ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE"></a>
|
||||
### `ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE`
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ initial input, or when referenced by `import` statements within ES module code:
|
||||
* Files ending in `.js` when the nearest parent `package.json` file contains a
|
||||
top-level field `"type"` with a value of `"module"`.
|
||||
|
||||
* Strings passed in as an argument to `--eval` or `--print`, or piped to
|
||||
`node` via `STDIN`, with the flag `--input-type=module`.
|
||||
* Strings passed in as an argument to `--eval`, or piped to `node` via `STDIN`,
|
||||
with the flag `--input-type=module`.
|
||||
|
||||
Node.js will treat as CommonJS all other forms of input, such as `.js` files
|
||||
where the nearest parent `package.json` file contains no top-level `"type"`
|
||||
@@ -52,8 +52,8 @@ or when referenced by `import` statements within ES module code:
|
||||
* Files ending in `.js` when the nearest parent `package.json` file contains a
|
||||
top-level field `"type"` with a value of `"commonjs"`.
|
||||
|
||||
* Strings passed in as an argument to `--eval` or `--print`, or piped to
|
||||
`node` via `STDIN`, with the flag `--input-type=commonjs`.
|
||||
* Strings passed in as an argument to `--eval` or `--print`, or piped to `node`
|
||||
via `STDIN`, with the flag `--input-type=commonjs`.
|
||||
|
||||
### `package.json` `"type"` field
|
||||
|
||||
@@ -159,9 +159,9 @@ package scope:
|
||||
|
||||
### `--input-type` flag
|
||||
|
||||
Strings passed in as an argument to `--eval` or `--print` (or `-e` or `-p`), or
|
||||
piped to `node` via `STDIN`, will be treated as ES modules when the
|
||||
`--input-type=module` flag is set.
|
||||
Strings passed in as an argument to `--eval` (or `-e`), or piped to `node` via
|
||||
`STDIN`, will be treated as ES modules when the `--input-type=module` flag is
|
||||
set.
|
||||
|
||||
```sh
|
||||
node --input-type=module --eval "import { sep } from 'path'; console.log(sep);"
|
||||
@@ -1076,6 +1076,32 @@ node --experimental-wasm-modules index.mjs
|
||||
|
||||
would provide the exports interface for the instantiation of `module.wasm`.
|
||||
|
||||
## Experimental Top-Level `await`
|
||||
|
||||
When the `--experimental-top-level-await` flag is provided, `await` may be used
|
||||
in the top level (outside of async functions) within modules. This implements
|
||||
the [ECMAScript Top-Level `await` proposal][].
|
||||
|
||||
Assuming an `a.mjs` with
|
||||
|
||||
<!-- eslint-skip -->
|
||||
```js
|
||||
export const five = await Promise.resolve(5);
|
||||
```
|
||||
|
||||
And a `b.mjs` with
|
||||
|
||||
```js
|
||||
import { five } from './a.mjs';
|
||||
|
||||
console.log(five); // Logs `5`
|
||||
```
|
||||
|
||||
```bash
|
||||
node b.mjs # fails
|
||||
node --experimental-top-level-await b.mjs # works
|
||||
```
|
||||
|
||||
## Experimental Loaders
|
||||
|
||||
**Note: This API is currently being redesigned and will still change.**
|
||||
@@ -1779,6 +1805,7 @@ success!
|
||||
[Conditional Exports]: #esm_conditional_exports
|
||||
[Dynamic `import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports
|
||||
[ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
|
||||
[ECMAScript Top-Level `await` proposal]: https://github.com/tc39/proposal-top-level-await/
|
||||
[ES Module Integration Proposal for Web Assembly]: https://github.com/webassembly/esm-integration
|
||||
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
|
||||
[Terminology]: #esm_terminology
|
||||
|
||||
@@ -402,7 +402,10 @@ support is planned.
|
||||
```js
|
||||
const vm = require('vm');
|
||||
|
||||
const contextifiedObject = vm.createContext({ secret: 42 });
|
||||
const contextifiedObject = vm.createContext({
|
||||
secret: 42,
|
||||
print: console.log,
|
||||
});
|
||||
|
||||
(async () => {
|
||||
// Step 1
|
||||
@@ -418,6 +421,7 @@ const contextifiedObject = vm.createContext({ secret: 42 });
|
||||
const bar = new vm.SourceTextModule(`
|
||||
import s from 'foo';
|
||||
s;
|
||||
print(s);
|
||||
`, { context: contextifiedObject });
|
||||
|
||||
// Step 2
|
||||
@@ -460,16 +464,11 @@ const contextifiedObject = vm.createContext({ secret: 42 });
|
||||
|
||||
// Step 3
|
||||
//
|
||||
// Evaluate the Module. The evaluate() method returns a Promise with a single
|
||||
// property "result" that contains the result of the very last statement
|
||||
// executed in the Module. In the case of `bar`, it is `s;`, which refers to
|
||||
// the default export of the `foo` module, the `secret` we set in the
|
||||
// beginning to 42.
|
||||
// Evaluate the Module. The evaluate() method returns a promise which will
|
||||
// resolve after the module has finished evaluating.
|
||||
|
||||
const { result } = await bar.evaluate();
|
||||
|
||||
console.log(result);
|
||||
// Prints 42.
|
||||
await bar.evaluate();
|
||||
})();
|
||||
```
|
||||
|
||||
@@ -512,17 +511,14 @@ in the ECMAScript specification.
|
||||
|
||||
Evaluate the module.
|
||||
|
||||
This must be called after the module has been linked; otherwise it will
|
||||
throw an error. It could be called also when the module has already been
|
||||
evaluated, in which case it will do one of the following two things:
|
||||
|
||||
* return `undefined` if the initial evaluation ended in success (`module.status`
|
||||
is `'evaluated'`)
|
||||
* rethrow the same exception the initial evaluation threw if the initial
|
||||
evaluation ended in an error (`module.status` is `'errored'`)
|
||||
This must be called after the module has been linked; otherwise it will reject.
|
||||
It could be called also when the module has already been evaluated, in which
|
||||
case it will either do nothing if the initial evaluation ended in success
|
||||
(`module.status` is `'evaluated'`) or it will re-throw the exception that the
|
||||
initial evaluation resulted in (`module.status` is `'errored'`).
|
||||
|
||||
This method cannot be called while the module is being evaluated
|
||||
(`module.status` is `'evaluating'`) to prevent infinite recursion.
|
||||
(`module.status` is `'evaluating'`).
|
||||
|
||||
Corresponds to the [Evaluate() concrete method][] field of [Cyclic Module
|
||||
Record][]s in the ECMAScript specification.
|
||||
|
||||
@@ -805,6 +805,7 @@ E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) {
|
||||
}, TypeError);
|
||||
E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported',
|
||||
RangeError);
|
||||
E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error);
|
||||
E('ERR_FALSY_VALUE_REJECTION', function(reason) {
|
||||
this.reason = reason;
|
||||
return 'Promise was rejected with falsy value';
|
||||
|
||||
@@ -167,10 +167,9 @@ class Loader {
|
||||
};
|
||||
const job = new ModuleJob(this, url, evalInstance, false, false);
|
||||
this.moduleMap.set(url, job);
|
||||
const { module, result } = await job.run();
|
||||
const { module } = await job.run();
|
||||
return {
|
||||
namespace: module.getNamespace(),
|
||||
result
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,21 +31,26 @@ class ModuleJob {
|
||||
this.isMain = isMain;
|
||||
this.inspectBrk = inspectBrk;
|
||||
|
||||
// This is a Promise<{ module, reflect }>, whose fields will be copied
|
||||
// onto `this` by `link()` below once it has been resolved.
|
||||
this.modulePromise = moduleProvider.call(loader, url, isMain);
|
||||
this.module = undefined;
|
||||
// Expose the promise to the ModuleWrap directly for linking below.
|
||||
// `this.module` is also filled in below.
|
||||
this.modulePromise = moduleProvider.call(loader, url, isMain);
|
||||
|
||||
// Wait for the ModuleWrap instance being linked with all dependencies.
|
||||
const link = async () => {
|
||||
this.module = await this.modulePromise;
|
||||
assert(this.module instanceof ModuleWrap);
|
||||
|
||||
// Explicitly keeping track of dependency jobs is needed in order
|
||||
// to flatten out the dependency graph below in `_instantiate()`,
|
||||
// so that circular dependencies can't cause a deadlock by two of
|
||||
// these `link` callbacks depending on each other.
|
||||
const dependencyJobs = [];
|
||||
const promises = this.module.link(async (specifier) => {
|
||||
const jobPromise = this.loader.getModuleJob(specifier, url);
|
||||
dependencyJobs.push(jobPromise);
|
||||
return (await jobPromise).modulePromise;
|
||||
const job = await jobPromise;
|
||||
return job.modulePromise;
|
||||
});
|
||||
|
||||
if (promises !== undefined)
|
||||
@@ -59,25 +64,20 @@ class ModuleJob {
|
||||
// 'unhandled rejection' warnings.
|
||||
this.linked.catch(noop);
|
||||
|
||||
// instantiated == deep dependency jobs wrappers instantiated,
|
||||
// module wrapper instantiated
|
||||
// instantiated == deep dependency jobs wrappers are instantiated,
|
||||
// and module wrapper is instantiated.
|
||||
this.instantiated = undefined;
|
||||
}
|
||||
|
||||
async instantiate() {
|
||||
if (!this.instantiated) {
|
||||
return this.instantiated = this._instantiate();
|
||||
instantiate() {
|
||||
if (this.instantiated === undefined) {
|
||||
this.instantiated = this._instantiate();
|
||||
}
|
||||
await this.instantiated;
|
||||
return this.module;
|
||||
return this.instantiated;
|
||||
}
|
||||
|
||||
// This method instantiates the module associated with this job and its
|
||||
// entire dependency graph, i.e. creates all the module namespaces and the
|
||||
// exported/imported variables.
|
||||
async _instantiate() {
|
||||
const jobsInGraph = new SafeSet();
|
||||
|
||||
const addJobsToDependencyGraph = async (moduleJob) => {
|
||||
if (jobsInGraph.has(moduleJob)) {
|
||||
return;
|
||||
@@ -87,6 +87,7 @@ class ModuleJob {
|
||||
return PromiseAll(dependencyJobs.map(addJobsToDependencyGraph));
|
||||
};
|
||||
await addJobsToDependencyGraph(this);
|
||||
|
||||
try {
|
||||
if (!hasPausedEntry && this.inspectBrk) {
|
||||
hasPausedEntry = true;
|
||||
@@ -122,19 +123,20 @@ class ModuleJob {
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
for (const dependencyJob of jobsInGraph) {
|
||||
// Calling `this.module.instantiate()` instantiates not only the
|
||||
// ModuleWrap in this module, but all modules in the graph.
|
||||
dependencyJob.instantiated = resolvedPromise;
|
||||
}
|
||||
return this.module;
|
||||
}
|
||||
|
||||
async run() {
|
||||
const module = await this.instantiate();
|
||||
await this.instantiate();
|
||||
const timeout = -1;
|
||||
const breakOnSigint = false;
|
||||
return { module, result: module.evaluate(timeout, breakOnSigint) };
|
||||
await this.module.evaluate(timeout, breakOnSigint);
|
||||
return { module: this.module };
|
||||
}
|
||||
}
|
||||
ObjectSetPrototypeOf(ModuleJob.prototype, null);
|
||||
|
||||
@@ -10,8 +10,9 @@ const path = require('path');
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET
|
||||
}
|
||||
ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET,
|
||||
ERR_EVAL_ESM_CANNOT_PRINT,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
|
||||
const {
|
||||
@@ -39,6 +40,9 @@ function tryGetCwd() {
|
||||
}
|
||||
|
||||
function evalModule(source, print) {
|
||||
if (print) {
|
||||
throw new ERR_EVAL_ESM_CANNOT_PRINT();
|
||||
}
|
||||
const { log, error } = require('internal/console/global');
|
||||
const { decorateErrorStack } = require('internal/util');
|
||||
const asyncESM = require('internal/process/esm_loader');
|
||||
|
||||
@@ -219,8 +219,7 @@ class Module {
|
||||
'must be one of linked, evaluated, or errored'
|
||||
);
|
||||
}
|
||||
const result = this[kWrap].evaluate(timeout, breakOnSigint);
|
||||
return { __proto__: null, result };
|
||||
await this[kWrap].evaluate(timeout, breakOnSigint);
|
||||
}
|
||||
|
||||
[customInspectSymbol](depth, options) {
|
||||
|
||||
@@ -375,7 +375,13 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(result.ToLocalChecked());
|
||||
// If TLA is enabled, `result` is the evaluation's promise.
|
||||
// Otherwise, `result` is the last evaluated value of the module,
|
||||
// which could be a promise, which would result in it being incorrectly
|
||||
// unwrapped when the higher level code awaits the evaluation.
|
||||
if (env->isolate_data()->options()->experimental_top_level_await) {
|
||||
args.GetReturnValue().Set(result.ToLocalChecked());
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
|
||||
@@ -387,13 +393,17 @@ void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
|
||||
switch (module->GetStatus()) {
|
||||
default:
|
||||
case v8::Module::Status::kUninstantiated:
|
||||
case v8::Module::Status::kInstantiating:
|
||||
return env->ThrowError(
|
||||
"cannot get namespace, Module has not been instantiated");
|
||||
"cannot get namespace, module has not been instantiated");
|
||||
case v8::Module::Status::kInstantiated:
|
||||
case v8::Module::Status::kEvaluating:
|
||||
case v8::Module::Status::kEvaluated:
|
||||
case v8::Module::Status::kErrored:
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Local<Value> result = module->GetModuleNamespace();
|
||||
@@ -616,19 +626,19 @@ MaybeLocal<Value> ModuleWrap::SyntheticModuleEvaluationStepsCallback(
|
||||
TryCatchScope try_catch(env);
|
||||
Local<Function> synthetic_evaluation_steps =
|
||||
obj->synthetic_evaluation_steps_.Get(isolate);
|
||||
obj->synthetic_evaluation_steps_.Reset();
|
||||
MaybeLocal<Value> ret = synthetic_evaluation_steps->Call(context,
|
||||
obj->object(), 0, nullptr);
|
||||
if (ret.IsEmpty()) {
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
obj->synthetic_evaluation_steps_.Reset();
|
||||
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
||||
CHECK(!try_catch.Message().IsEmpty());
|
||||
CHECK(!try_catch.Exception().IsEmpty());
|
||||
try_catch.ReThrow();
|
||||
return MaybeLocal<Value>();
|
||||
}
|
||||
return ret;
|
||||
return Undefined(isolate);
|
||||
}
|
||||
|
||||
void ModuleWrap::SetSyntheticExport(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
@@ -138,10 +138,9 @@ void OptionsParser<Options>::Implies(const char* from,
|
||||
const char* to) {
|
||||
auto it = options_.find(to);
|
||||
CHECK_NE(it, options_.end());
|
||||
CHECK_EQ(it->second.type, kBoolean);
|
||||
implications_.emplace(from, Implication {
|
||||
it->second.field, true
|
||||
});
|
||||
CHECK(it->second.type == kBoolean || it->second.type == kV8Option);
|
||||
implications_.emplace(
|
||||
from, Implication{it->second.type, to, it->second.field, true});
|
||||
}
|
||||
|
||||
template <typename Options>
|
||||
@@ -150,9 +149,8 @@ void OptionsParser<Options>::ImpliesNot(const char* from,
|
||||
auto it = options_.find(to);
|
||||
CHECK_NE(it, options_.end());
|
||||
CHECK_EQ(it->second.type, kBoolean);
|
||||
implications_.emplace(from, Implication {
|
||||
it->second.field, false
|
||||
});
|
||||
implications_.emplace(
|
||||
from, Implication{it->second.type, to, it->second.field, false});
|
||||
}
|
||||
|
||||
template <typename Options>
|
||||
@@ -196,9 +194,11 @@ template <typename ChildOptions>
|
||||
auto OptionsParser<Options>::Convert(
|
||||
typename OptionsParser<ChildOptions>::Implication original,
|
||||
ChildOptions* (Options::* get_child)()) {
|
||||
return Implication {
|
||||
Convert(original.target_field, get_child),
|
||||
original.target_value
|
||||
return Implication{
|
||||
original.type,
|
||||
original.name,
|
||||
Convert(original.target_field, get_child),
|
||||
original.target_value,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -366,19 +366,23 @@ void OptionsParser<Options>::Parse(
|
||||
break;
|
||||
}
|
||||
|
||||
if (it == options_.end()) {
|
||||
v8_args->push_back(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
auto implications = implications_.equal_range(name);
|
||||
for (auto it = implications.first; it != implications.second; ++it) {
|
||||
*it->second.target_field->template Lookup<bool>(options) =
|
||||
it->second.target_value;
|
||||
if (it->second.type == kV8Option) {
|
||||
v8_args->push_back(it->second.name);
|
||||
} else {
|
||||
*it->second.target_field->template Lookup<bool>(options) =
|
||||
it->second.target_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (it == options_.end()) {
|
||||
v8_args->push_back(arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
const OptionInfo& info = it->second;
|
||||
std::string value;
|
||||
if (info.type != kBoolean && info.type != kNoOp && info.type != kV8Option) {
|
||||
|
||||
@@ -593,6 +593,13 @@ PerIsolateOptionsParser::PerIsolateOptionsParser(
|
||||
kAllowedInEnvironment);
|
||||
Implies("--report-signal", "--report-on-signal");
|
||||
|
||||
AddOption("--experimental-top-level-await",
|
||||
"enable experimental support for ECMAScript Top-Level Await",
|
||||
&PerIsolateOptions::experimental_top_level_await);
|
||||
AddOption("--harmony-top-level-await", "", V8Option{});
|
||||
Implies("--experimental-top-level-await", "--harmony-top-level-await");
|
||||
Implies("--harmony-top-level-await", "--experimental-top-level-await");
|
||||
|
||||
Insert(eop, &PerIsolateOptions::get_per_env_options);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,7 @@ class PerIsolateOptions : public Options {
|
||||
bool no_node_snapshot = false;
|
||||
bool report_uncaught_exception = false;
|
||||
bool report_on_signal = false;
|
||||
bool experimental_top_level_await = false;
|
||||
std::string report_signal = "SIGUSR2";
|
||||
inline EnvironmentOptions* get_per_env_options();
|
||||
void CheckOptions(std::vector<std::string>* errors) override;
|
||||
@@ -418,6 +419,8 @@ class OptionsParser {
|
||||
// An implied option is composed of the information on where to store a
|
||||
// specific boolean value (if another specific option is encountered).
|
||||
struct Implication {
|
||||
OptionType type;
|
||||
std::string name;
|
||||
std::shared_ptr<BaseOptionField> target_field;
|
||||
bool target_value;
|
||||
};
|
||||
|
||||
11
test/es-module/test-esm-tla.mjs
Normal file
11
test/es-module/test-esm-tla.mjs
Normal file
@@ -0,0 +1,11 @@
|
||||
// Flags: --experimental-top-level-await
|
||||
|
||||
import '../common/index.mjs';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import assert from 'assert';
|
||||
import { pathToFileURL } from 'url';
|
||||
|
||||
import(pathToFileURL(fixtures.path('/es-modules/tla/parent.mjs')))
|
||||
.then(({ default: order }) => {
|
||||
assert.deepStrictEqual(order, ['order', 'b', 'c', 'd', 'a', 'parent']);
|
||||
});
|
||||
7
test/fixtures/es-modules/tla/a.mjs
vendored
Normal file
7
test/fixtures/es-modules/tla/a.mjs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import order from './order.mjs';
|
||||
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
|
||||
order.push('a');
|
||||
3
test/fixtures/es-modules/tla/b.mjs
vendored
Normal file
3
test/fixtures/es-modules/tla/b.mjs
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import order from './order.mjs';
|
||||
|
||||
order.push('b');
|
||||
3
test/fixtures/es-modules/tla/c.mjs
vendored
Normal file
3
test/fixtures/es-modules/tla/c.mjs
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import order from './order.mjs';
|
||||
|
||||
order.push('c');
|
||||
6
test/fixtures/es-modules/tla/d.mjs
vendored
Normal file
6
test/fixtures/es-modules/tla/d.mjs
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import order from './order.mjs';
|
||||
|
||||
const end = Date.now() + 500;
|
||||
while (end < Date.now()) {}
|
||||
|
||||
order.push('d');
|
||||
1
test/fixtures/es-modules/tla/order.mjs
vendored
Normal file
1
test/fixtures/es-modules/tla/order.mjs
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export default ['order'];
|
||||
9
test/fixtures/es-modules/tla/parent.mjs
vendored
Normal file
9
test/fixtures/es-modules/tla/parent.mjs
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import order from './order.mjs';
|
||||
import './a.mjs';
|
||||
import './b.mjs';
|
||||
import './c.mjs';
|
||||
import './d.mjs';
|
||||
|
||||
order.push('parent');
|
||||
|
||||
export default order;
|
||||
@@ -245,9 +245,10 @@ child.exec(
|
||||
// Assert that "42\n" is written to stdout with print option.
|
||||
child.exec(
|
||||
`${nodejs} ${execOptions} --print --eval "42"`,
|
||||
common.mustCall((err, stdout) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(stdout, '42\n');
|
||||
common.mustCall((err, stdout, stderr) => {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.ok(stderr.includes('--print cannot be used with ESM input'));
|
||||
}));
|
||||
|
||||
// Assert that error is written to stderr on invalid input.
|
||||
|
||||
@@ -10,7 +10,7 @@ const { ModuleWrap } = internalBinding('module_wrap');
|
||||
const { getPromiseDetails, isPromise } = internalBinding('util');
|
||||
const setTimeoutAsync = require('util').promisify(setTimeout);
|
||||
|
||||
const foo = new ModuleWrap('foo', undefined, 'export * from "bar"; 6;', 0, 0);
|
||||
const foo = new ModuleWrap('foo', undefined, 'export * from "bar";', 0, 0);
|
||||
const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
|
||||
|
||||
(async () => {
|
||||
@@ -24,6 +24,6 @@ const bar = new ModuleWrap('bar', undefined, 'export const five = 5', 0, 0);
|
||||
|
||||
foo.instantiate();
|
||||
|
||||
assert.strictEqual(await foo.evaluate(-1, false), 6);
|
||||
assert.strictEqual(await foo.evaluate(-1, false), undefined);
|
||||
assert.strictEqual(foo.getNamespace().five, 5);
|
||||
})();
|
||||
|
||||
@@ -26,26 +26,26 @@ const util = require('util');
|
||||
assert.strictEqual(m.status, 'unlinked');
|
||||
await m.link(common.mustNotCall());
|
||||
assert.strictEqual(m.status, 'linked');
|
||||
const result = await m.evaluate();
|
||||
assert.strictEqual(await m.evaluate(), undefined);
|
||||
assert.strictEqual(m.status, 'evaluated');
|
||||
assert.strictEqual(Object.getPrototypeOf(result), null);
|
||||
assert.deepStrictEqual(context, {
|
||||
foo: 'bar',
|
||||
baz: 'bar',
|
||||
typeofProcess: 'undefined'
|
||||
});
|
||||
assert.strictEqual(result.result, 'function');
|
||||
}());
|
||||
|
||||
(async () => {
|
||||
const m = new SourceTextModule(
|
||||
'global.vmResult = "foo"; Object.prototype.toString.call(process);'
|
||||
);
|
||||
const m = new SourceTextModule(`
|
||||
global.vmResultFoo = "foo";
|
||||
global.vmResultTypeofProcess = Object.prototype.toString.call(process);
|
||||
`);
|
||||
await m.link(common.mustNotCall());
|
||||
const { result } = await m.evaluate();
|
||||
assert.strictEqual(global.vmResult, 'foo');
|
||||
assert.strictEqual(result, '[object process]');
|
||||
delete global.vmResult;
|
||||
await m.evaluate();
|
||||
assert.strictEqual(global.vmResultFoo, 'foo');
|
||||
assert.strictEqual(global.vmResultTypeofProcess, '[object process]');
|
||||
delete global.vmResultFoo;
|
||||
delete global.vmResultTypeofProcess;
|
||||
})();
|
||||
|
||||
(async () => {
|
||||
|
||||
@@ -5,19 +5,23 @@
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const { Script, SourceTextModule, createContext } = require('vm');
|
||||
const { Script, SourceTextModule } = require('vm');
|
||||
|
||||
async function testNoCallback() {
|
||||
const m = new SourceTextModule('import("foo")', { context: createContext() });
|
||||
const m = new SourceTextModule(`
|
||||
globalThis.importResult = import("foo");
|
||||
globalThis.importResult.catch(() => {});
|
||||
`);
|
||||
await m.link(common.mustNotCall());
|
||||
const { result } = await m.evaluate();
|
||||
await m.evaluate();
|
||||
let threw = false;
|
||||
try {
|
||||
await result;
|
||||
await globalThis.importResult;
|
||||
} catch (err) {
|
||||
threw = true;
|
||||
assert.strictEqual(err.code, 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING');
|
||||
}
|
||||
delete globalThis.importResult;
|
||||
assert(threw);
|
||||
}
|
||||
|
||||
@@ -40,7 +44,7 @@ async function test() {
|
||||
}
|
||||
|
||||
{
|
||||
const m = new SourceTextModule('import("foo")', {
|
||||
const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
assert.strictEqual(specifier, 'foo');
|
||||
assert.strictEqual(wrap, m);
|
||||
@@ -48,24 +52,26 @@ async function test() {
|
||||
}),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
const { result } = await m.evaluate();
|
||||
assert.strictEqual(foo.namespace, await result);
|
||||
await m.evaluate();
|
||||
assert.strictEqual(foo.namespace, await globalThis.fooResult);
|
||||
delete globalThis.fooResult;
|
||||
}
|
||||
}
|
||||
|
||||
async function testInvalid() {
|
||||
const m = new SourceTextModule('import("foo")', {
|
||||
const m = new SourceTextModule('globalThis.fooResult = import("foo")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
return 5;
|
||||
}),
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
const { result } = await m.evaluate();
|
||||
await result.catch(common.mustCall((e) => {
|
||||
await m.evaluate();
|
||||
await globalThis.fooResult.catch(common.mustCall((e) => {
|
||||
assert.strictEqual(e.code, 'ERR_VM_MODULE_NOT_MODULE');
|
||||
}));
|
||||
delete globalThis.fooResult;
|
||||
|
||||
const s = new Script('import("foo")', {
|
||||
const s = new Script('import("bar")', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
return undefined;
|
||||
}),
|
||||
|
||||
@@ -9,22 +9,18 @@ const assert = require('assert');
|
||||
const { types } = require('util');
|
||||
const { SourceTextModule } = require('vm');
|
||||
|
||||
async function getNamespace() {
|
||||
const m = new SourceTextModule('');
|
||||
await m.link(() => 0);
|
||||
await m.evaluate();
|
||||
return m.namespace;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const namespace = await getNamespace();
|
||||
const m = new SourceTextModule('export const A = "A"; import("");', {
|
||||
importModuleDynamically: common.mustCall((specifier, wrap) => {
|
||||
return namespace;
|
||||
})
|
||||
const m = new SourceTextModule('globalThis.importResult = import("");', {
|
||||
importModuleDynamically: common.mustCall(async (specifier, wrap) => {
|
||||
const m = new SourceTextModule('');
|
||||
await m.link(() => 0);
|
||||
await m.evaluate();
|
||||
return m.namespace;
|
||||
}),
|
||||
});
|
||||
await m.link(() => 0);
|
||||
const { result } = await m.evaluate();
|
||||
const ns = await result;
|
||||
await m.evaluate();
|
||||
const ns = await globalThis.importResult;
|
||||
delete globalThis.importResult;
|
||||
assert.ok(types.isModuleNamespaceObject(ns));
|
||||
})().then(common.mustCall());
|
||||
|
||||
@@ -182,13 +182,11 @@ async function checkExecution() {
|
||||
await (async () => {
|
||||
const m = new SourceTextModule('throw new Error();');
|
||||
await m.link(common.mustNotCall());
|
||||
const evaluatePromise = m.evaluate();
|
||||
await evaluatePromise.catch(() => {});
|
||||
assert.strictEqual(m.status, 'errored');
|
||||
try {
|
||||
await evaluatePromise;
|
||||
await m.evaluate();
|
||||
} catch (err) {
|
||||
assert.strictEqual(m.error, err);
|
||||
assert.strictEqual(m.status, 'errored');
|
||||
return;
|
||||
}
|
||||
assert.fail('Missing expected exception');
|
||||
|
||||
@@ -7,14 +7,16 @@ const assert = require('assert');
|
||||
const { SourceTextModule } = require('vm');
|
||||
|
||||
async function testBasic() {
|
||||
const m = new SourceTextModule('import.meta;', {
|
||||
const m = new SourceTextModule('globalThis.importMeta = import.meta;', {
|
||||
initializeImportMeta: common.mustCall((meta, module) => {
|
||||
assert.strictEqual(module, m);
|
||||
meta.prop = 42;
|
||||
})
|
||||
});
|
||||
await m.link(common.mustNotCall());
|
||||
const { result } = await m.evaluate();
|
||||
await m.evaluate();
|
||||
const result = globalThis.importMeta;
|
||||
delete globalThis.importMeta;
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.strictEqual(Object.getPrototypeOf(result), null);
|
||||
assert.strictEqual(result.prop, 42);
|
||||
|
||||
@@ -13,7 +13,8 @@ async function simple() {
|
||||
const foo = new SourceTextModule('export default 5;');
|
||||
await foo.link(common.mustNotCall());
|
||||
|
||||
const bar = new SourceTextModule('import five from "foo"; five');
|
||||
globalThis.fiveResult = undefined;
|
||||
const bar = new SourceTextModule('import five from "foo"; fiveResult = five');
|
||||
|
||||
assert.deepStrictEqual(bar.dependencySpecifiers, ['foo']);
|
||||
|
||||
@@ -23,7 +24,9 @@ async function simple() {
|
||||
return foo;
|
||||
}));
|
||||
|
||||
assert.strictEqual((await bar.evaluate()).result, 5);
|
||||
await bar.evaluate();
|
||||
assert.strictEqual(globalThis.fiveResult, 5);
|
||||
delete globalThis.fiveResult;
|
||||
}
|
||||
|
||||
async function depth() {
|
||||
|
||||
@@ -12,11 +12,16 @@ const finished = common.mustCall();
|
||||
|
||||
(async function main() {
|
||||
{
|
||||
const m = new SourceTextModule('1');
|
||||
globalThis.count = 0;
|
||||
const m = new SourceTextModule('count += 1;');
|
||||
await m.link(common.mustNotCall());
|
||||
assert.strictEqual((await m.evaluate()).result, 1);
|
||||
assert.strictEqual((await m.evaluate()).result, undefined);
|
||||
assert.strictEqual((await m.evaluate()).result, undefined);
|
||||
assert.strictEqual(await m.evaluate(), undefined);
|
||||
assert.strictEqual(globalThis.count, 1);
|
||||
assert.strictEqual(await m.evaluate(), undefined);
|
||||
assert.strictEqual(globalThis.count, 1);
|
||||
assert.strictEqual(await m.evaluate(), undefined);
|
||||
assert.strictEqual(globalThis.count, 1);
|
||||
delete globalThis.count;
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Flags: --experimental-vm-modules
|
||||
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
const { SyntheticModule, SourceTextModule } = require('vm');
|
||||
const assert = require('assert');
|
||||
|
||||
@@ -26,6 +26,17 @@ const assert = require('assert');
|
||||
assert.strictEqual(m.namespace.getX(), 42);
|
||||
}
|
||||
|
||||
{
|
||||
const s = new SyntheticModule([], () => {
|
||||
const p = Promise.reject();
|
||||
p.catch(() => {});
|
||||
return p;
|
||||
});
|
||||
|
||||
await s.link(common.mustNotCall());
|
||||
assert.strictEqual(await s.evaluate(), undefined);
|
||||
}
|
||||
|
||||
for (const invalidName of [1, Symbol.iterator, {}, [], null, true, 0]) {
|
||||
const s = new SyntheticModule([], () => {});
|
||||
await s.link(() => {});
|
||||
|
||||
Reference in New Issue
Block a user