summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorGus Caplan <me@gus.host>2018-01-13 23:35:51 -0800
committerTimothy Gu <timothygu99@gmail.com>2018-01-30 17:00:57 -0800
commit0993fbe5b213d0fe746c3162bcda85f6c66bb552 (patch)
treef0a1ffef2ab24f1da963a23a43f3dd58eed5bba0 /lib
parent2033a9f4362ee46b0fbddf9caf9cf6238391561e (diff)
downloadandroid-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.tar.gz
android-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.tar.bz2
android-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.zip
vm: add modules
Adds vm.Module, which wraps around ModuleWrap to provide an interface for developers to work with modules in a more reflective manner. Co-authored-by: Timothy Gu <timothygu99@gmail.com> PR-URL: https://github.com/nodejs/node/pull/17560 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/errors.js9
-rw-r--r--lib/internal/vm/Module.js205
-rw-r--r--lib/vm.js5
3 files changed, 218 insertions, 1 deletions
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index 6a52d55bb8..3530d63710 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -676,6 +676,15 @@ E('ERR_V8BREAKITERATOR', 'Full ICU data not installed. ' +
'See https://github.com/nodejs/node/wiki/Intl');
E('ERR_VALID_PERFORMANCE_ENTRY_TYPE',
'At least one valid performance entry type is required');
+E('ERR_VM_MODULE_ALREADY_LINKED', 'Module has already been linked');
+E('ERR_VM_MODULE_DIFFERENT_CONTEXT',
+ 'Linked modules must use the same context');
+E('ERR_VM_MODULE_LINKING_ERRORED',
+ 'Linking has already failed for the provided module');
+E('ERR_VM_MODULE_NOT_LINKED',
+ 'Module must be linked before it can be instantiated');
+E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module');
+E('ERR_VM_MODULE_STATUS', 'Module status %s');
E('ERR_ZLIB_BINDING_CLOSED', 'zlib binding closed');
E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed');
diff --git a/lib/internal/vm/Module.js b/lib/internal/vm/Module.js
new file mode 100644
index 0000000000..a8625ef76b
--- /dev/null
+++ b/lib/internal/vm/Module.js
@@ -0,0 +1,205 @@
+'use strict';
+
+const { emitExperimentalWarning } = require('internal/util');
+const { URL } = require('internal/url');
+const { kParsingContext, isContext } = process.binding('contextify');
+const errors = require('internal/errors');
+const {
+ getConstructorOf,
+ customInspectSymbol,
+} = require('internal/util');
+
+const {
+ ModuleWrap,
+ kUninstantiated,
+ kInstantiating,
+ kInstantiated,
+ kEvaluating,
+ kEvaluated,
+ kErrored,
+} = internalBinding('module_wrap');
+
+const STATUS_MAP = {
+ [kUninstantiated]: 'uninstantiated',
+ [kInstantiating]: 'instantiating',
+ [kInstantiated]: 'instantiated',
+ [kEvaluating]: 'evaluating',
+ [kEvaluated]: 'evaluated',
+ [kErrored]: 'errored',
+};
+
+let globalModuleId = 0;
+const perContextModuleId = new WeakMap();
+const wrapMap = new WeakMap();
+const dependencyCacheMap = new WeakMap();
+const linkingStatusMap = new WeakMap();
+
+class Module {
+ constructor(src, options = {}) {
+ emitExperimentalWarning('vm.Module');
+
+ if (typeof src !== 'string')
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE', 'src', 'string', src);
+ if (typeof options !== 'object' || options === null)
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE', 'options', 'object', options);
+
+ let context;
+ if (options.context !== undefined) {
+ if (isContext(options.context)) {
+ context = options.context;
+ } else {
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE', 'options.context', 'vm.Context');
+ }
+ }
+
+ let url = options.url;
+ if (url !== undefined) {
+ if (typeof url !== 'string') {
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE', 'options.url', 'string', url);
+ }
+ url = new URL(url).href;
+ } else if (context === undefined) {
+ url = `vm:module(${globalModuleId++})`;
+ } else if (perContextModuleId.has(context)) {
+ const curId = perContextModuleId.get(context);
+ url = `vm:module(${curId})`;
+ perContextModuleId.set(context, curId + 1);
+ } else {
+ url = 'vm:module(0)';
+ perContextModuleId.set(context, 1);
+ }
+
+ const wrap = new ModuleWrap(src, url, {
+ [kParsingContext]: context,
+ lineOffset: options.lineOffset,
+ columnOffset: options.columnOffset
+ });
+
+ wrapMap.set(this, wrap);
+ linkingStatusMap.set(this, 'unlinked');
+
+ Object.defineProperties(this, {
+ url: { value: url, enumerable: true },
+ context: { value: context, enumerable: true },
+ });
+ }
+
+ get linkingStatus() {
+ return linkingStatusMap.get(this);
+ }
+
+ get status() {
+ return STATUS_MAP[wrapMap.get(this).getStatus()];
+ }
+
+ get namespace() {
+ const wrap = wrapMap.get(this);
+ if (wrap.getStatus() < kInstantiated)
+ throw new errors.Error('ERR_VM_MODULE_STATUS',
+ 'must not be uninstantiated or instantiating');
+ return wrap.namespace();
+ }
+
+ get dependencySpecifiers() {
+ let deps = dependencyCacheMap.get(this);
+ if (deps !== undefined)
+ return deps;
+
+ deps = wrapMap.get(this).getStaticDependencySpecifiers();
+ Object.freeze(deps);
+ dependencyCacheMap.set(this, deps);
+ return deps;
+ }
+
+ get error() {
+ const wrap = wrapMap.get(this);
+ if (wrap.getStatus() !== kErrored)
+ throw new errors.Error('ERR_VM_MODULE_STATUS', 'must be errored');
+ return wrap.getError();
+ }
+
+ async link(linker) {
+ if (typeof linker !== 'function')
+ throw new errors.TypeError(
+ 'ERR_INVALID_ARG_TYPE', 'linker', 'function', linker);
+ if (linkingStatusMap.get(this) !== 'unlinked')
+ throw new errors.Error('ERR_VM_MODULE_ALREADY_LINKED');
+ const wrap = wrapMap.get(this);
+ if (wrap.getStatus() !== kUninstantiated)
+ throw new errors.Error('ERR_VM_MODULE_STATUS', 'must be uninstantiated');
+ linkingStatusMap.set(this, 'linking');
+ const promises = [];
+ wrap.link((specifier) => {
+ const p = (async () => {
+ const m = await linker(this, specifier);
+ if (!m || !wrapMap.has(m))
+ throw new errors.Error('ERR_VM_MODULE_NOT_MODULE');
+ if (m.context !== this.context)
+ throw new errors.Error('ERR_VM_MODULE_DIFFERENT_CONTEXT');
+ const childLinkingStatus = linkingStatusMap.get(m);
+ if (childLinkingStatus === 'errored')
+ throw new errors.Error('ERR_VM_MODULE_LINKING_ERRORED');
+ if (childLinkingStatus === 'unlinked')
+ await m.link(linker);
+ return wrapMap.get(m);
+ })();
+ promises.push(p);
+ return p;
+ });
+ try {
+ await Promise.all(promises);
+ linkingStatusMap.set(this, 'linked');
+ } catch (err) {
+ linkingStatusMap.set(this, 'errored');
+ throw err;
+ }
+ }
+
+ instantiate() {
+ const wrap = wrapMap.get(this);
+ const status = wrap.getStatus();
+ if (status === kInstantiating || status === kEvaluating)
+ throw new errors.Error(
+ 'ERR_VM_MODULE_STATUS', 'must not be instantiating or evaluating');
+ if (linkingStatusMap.get(this) !== 'linked')
+ throw new errors.Error('ERR_VM_MODULE_NOT_LINKED');
+ wrap.instantiate();
+ }
+
+ async evaluate(options) {
+ const wrap = wrapMap.get(this);
+ const status = wrap.getStatus();
+ if (status !== kInstantiated &&
+ status !== kEvaluated &&
+ status !== kErrored) {
+ throw new errors.Error(
+ 'ERR_VM_MODULE_STATUS',
+ 'must be one of instantiated, evaluated, and errored');
+ }
+ const result = wrap.evaluate(options);
+ return { result, __proto__: null };
+ }
+
+ [customInspectSymbol](depth, options) {
+ let ctor = getConstructorOf(this);
+ ctor = ctor === null ? Module : ctor;
+
+ if (typeof depth === 'number' && depth < 0)
+ return options.stylize(`[${ctor.name}]`, 'special');
+
+ const o = Object.create({ constructor: ctor });
+ o.status = this.status;
+ o.linkingStatus = this.linkingStatus;
+ o.url = this.url;
+ o.context = this.context;
+ return require('util').inspect(o, options);
+ }
+}
+
+module.exports = {
+ Module
+};
diff --git a/lib/vm.js b/lib/vm.js
index ec05ad774a..c9bb44f057 100644
--- a/lib/vm.js
+++ b/lib/vm.js
@@ -192,5 +192,8 @@ module.exports = {
runInContext,
runInNewContext,
runInThisContext,
- isContext
+ isContext,
};
+
+if (process.binding('config').experimentalVMModules)
+ module.exports.Module = require('internal/vm/Module').Module;