Skip to content

Commit

Permalink
worker_threads: add support for data URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
aduh95 committed May 30, 2020
1 parent 4ed31e6 commit 2e5f780
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 22 deletions.
13 changes: 8 additions & 5 deletions doc/api/worker_threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,8 @@ changes:
* `filename` {string|URL} The path to the Worker’s main script or module. Must
be either an absolute path or a relative path (i.e. relative to the
current working directory) starting with `./` or `../`, or a WHATWG `URL`
object using `file:` protocol.
object using `file:` or `data:` protocol.
When using a [`data:` URL][], you must set the `type` option to `"module"`.
If `options.eval` is `true`, this is a string containing JavaScript code
rather than a path.
* `options` {Object}
Expand All @@ -601,12 +602,13 @@ changes:
to specify that the parent thread and the child thread should share their
environment variables; in that case, changes to one thread’s `process.env`
object will affect the other thread as well. **Default:** `process.env`.
* `eval` {boolean} If `true`, interpret the first argument to the constructor
* `eval` {boolean} If `true`, interprets the first argument to the constructor
as a script (or a module if `type` is set to `module`) that is executed once
the worker is online.
* `type` {string} If `"module"` and `eval` is set to `true`, interpret the
first argument as an ECMAScript 2015 module instead of a script. The default
value is `"classic"`. Doesn't have any effect when `eval` is set to `false`.
* `type` {string} If `"module"`, interprets the first argument as an
ECMAScript 2015 module instead of a script. The default value is
`"classic"`. Doesn't have any effect when `filename` refers to a file, in
which case the type is inferred from the file extension.
* `execArgv` {string[]} List of node CLI options passed to the worker.
V8 options (such as `--max-old-space-size`) and options that affect the
process (such as `--title`) are not supported. If set, this will be provided
Expand Down Expand Up @@ -869,3 +871,4 @@ active handle in the event system. If the worker is already `unref()`ed calling
[child processes]: child_process.html
[contextified]: vm.html#vm_what_does_it_mean_to_contextify_an_object
[v8.serdes]: v8.html#v8_serialization_api
[`data:` URL]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
18 changes: 16 additions & 2 deletions lib/internal/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

const {
ArrayIsArray,
JSONStringify,
MathMax,
ObjectCreate,
ObjectEntries,
Expand Down Expand Up @@ -99,7 +100,7 @@ class Worker extends EventEmitter {
argv = options.argv.map(String);
}

let url;
let url, doEval;
if (options.eval) {
if (typeof filename !== 'string') {
throw new ERR_INVALID_ARG_VALUE(
Expand All @@ -109,7 +110,20 @@ class Worker extends EventEmitter {
);
}
url = null;
doEval = true;
} else if (filename?.protocol === 'data:') {
if (options.type !== 'module') {
throw new ERR_INVALID_ARG_VALUE(
'options.type',
options.type,
'must be set to "module" when filename is a data: URL'
);
}
url = null;
doEval = true;
filename = `import ${JSONStringify(filename)}`;
} else {
doEval = false;
if (isURLInstance(filename)) {
url = filename;
filename = fileURLToPath(filename);
Expand Down Expand Up @@ -207,7 +221,7 @@ class Worker extends EventEmitter {
argv,
type: messageTypes.LOAD_SCRIPT,
filename,
doEval: !!options.eval,
doEval,
workerType: options.type,
cwdCounter: cwdCounter || workerIo.sharedCwdCounter,
workerData: options.workerData,
Expand Down
50 changes: 37 additions & 13 deletions test/parallel/test-worker-eval.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const cjsLoadingESMError = /Unexpected token 'export'/;
const esmLoadingCJSError = /module is not defined/;
const invalidValueError =
/The argument 'options\.type' must be either "module" or "classic"/;
const invalidValueError2 =
/The argument 'options\.type' must be set to "module" when filename is a data: URL/;

function runInWorker(evalString, options) {
return new Promise((resolve, reject) => {
Expand All @@ -19,20 +21,42 @@ function runInWorker(evalString, options) {
});
}

let options;
{
const options = { eval: true };
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
runInWorker(cjsString, options); // does not reject
}

options = { eval: true };
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
runInWorker(cjsString, options); // does not reject
{
const options = { eval: true, type: 'classic' };
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
runInWorker(cjsString, options); // does not reject
}

options = { eval: true, type: 'classic' };
assert.rejects(runInWorker(moduleString, options), cjsLoadingESMError);
runInWorker(cjsString, options); // does not reject
{
const options = { eval: true, type: 'module' };
runInWorker(moduleString, options); // does not reject
assert.rejects(runInWorker(cjsString, options), esmLoadingCJSError);
}

options = { eval: true, type: 'module' };
runInWorker(moduleString, options); // does not reject
assert.rejects(runInWorker(cjsString, options), esmLoadingCJSError);
{
const options = { eval: false, type: 'module' };
runInWorker(new URL(`data:text/javascript,${moduleString}`),
options); // does not reject
assert.rejects(runInWorker(new URL(`data:text/javascript,${cjsString}`),
options), esmLoadingCJSError);
}

options = { eval: true, type: 'wasm' };
assert.rejects(runInWorker(moduleString, options), invalidValueError);
assert.rejects(runInWorker(cjsString, options), invalidValueError);
{
const options = { eval: true, type: 'wasm' };
assert.rejects(runInWorker(moduleString, options), invalidValueError);
assert.rejects(runInWorker(cjsString, options), invalidValueError);
}

{
const options = { type: 'classic' };
assert.rejects(runInWorker(new URL(`data:text/javascript,${moduleString}`),
options), invalidValueError2);
assert.rejects(runInWorker(new URL(`data:text/javascript,${cjsString}`),
options), invalidValueError2);
}
2 changes: 0 additions & 2 deletions test/parallel/test-worker-unsupported-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,4 @@ const { Worker } = require('worker_threads');
};
assert.throws(() => { new Worker(new URL('https://www.url.com')); },
expectedErr);
assert.throws(() => { new Worker(new URL('data:application/javascript,')); },
expectedErr);
}

0 comments on commit 2e5f780

Please sign in to comment.