mirror of
https://github.com/oven-sh/bun
synced 2026-02-05 08:28:55 +00:00
Compare commits
7 Commits
ciro/fix-a
...
ben/worker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf1863877 | ||
|
|
fb11a3cada | ||
|
|
8f8c6c3811 | ||
|
|
f671455a5e | ||
|
|
15a16d4e79 | ||
|
|
cb7a8d9b7c | ||
|
|
35c7b5ad51 |
@@ -2023,6 +2023,11 @@ export fn Bun__VirtualMachine__setOverrideModuleRunMainPromise(vm: *VirtualMachi
|
||||
}
|
||||
}
|
||||
|
||||
export fn Bun__VirtualMachine__getWorker(vm: *VirtualMachine) ?*anyopaque {
|
||||
const worker = vm.worker orelse return null;
|
||||
return worker.cpp_worker;
|
||||
}
|
||||
|
||||
pub fn reloadEntryPointForTestRunner(this: *VirtualMachine, entry_path: []const u8) !*JSInternalPromise {
|
||||
this.has_loaded = false;
|
||||
this.main = entry_path;
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <JavaScriptCore/StructureCache.h>
|
||||
|
||||
#include <webcore/SerializedScriptValue.h>
|
||||
#include <webcore/Worker.h>
|
||||
#include "ProcessBindingTTYWrap.h"
|
||||
#include "wtf/text/ASCIILiteral.h"
|
||||
#include "wtf/text/StringToIntegerConversion.h"
|
||||
@@ -664,7 +665,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
|
||||
return JSValue::encode(resultValue);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, (JSGlobalObject * jsGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
if (callFrame->argumentCount() == 0 || callFrame->argument(0).isUndefined()) {
|
||||
mode_t currentMask = umask(0);
|
||||
@@ -672,10 +673,15 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, (JSGlobalObject * globalObject,
|
||||
return JSValue::encode(jsNumber(currentMask));
|
||||
}
|
||||
|
||||
auto* globalObject = defaultGlobalObject(jsGlobalObject);
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto value = callFrame->argument(0);
|
||||
|
||||
if (globalObject->worker()) {
|
||||
return Bun::ERR::WORKER_UNSUPPORTED_OPERATION(throwScope, globalObject, "Setting process.umask()"_s);
|
||||
}
|
||||
|
||||
auto value = callFrame->argument(0);
|
||||
mode_t newUmask;
|
||||
if (value.isString()) {
|
||||
auto str = value.getString(globalObject);
|
||||
@@ -3320,7 +3326,7 @@ static JSValue constructFeatures(VM& vm, JSObject* processObject)
|
||||
return object;
|
||||
}
|
||||
|
||||
static uint16_t debugPort;
|
||||
static uint16_t debugPort = 9229;
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(processDebugPort, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName))
|
||||
{
|
||||
@@ -3576,8 +3582,6 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu
|
||||
|
||||
/* Source for Process.lut.h
|
||||
@begin processObjectTable
|
||||
_debugEnd Process_stubEmptyFunction Function 0
|
||||
_debugProcess Process_stubEmptyFunction Function 0
|
||||
_fatalException Process_stubEmptyFunction Function 1
|
||||
_getActiveHandles Process_stubFunctionReturningArray Function 0
|
||||
_getActiveRequests Process_stubFunctionReturningArray Function 0
|
||||
@@ -3585,8 +3589,6 @@ extern "C" void Process__emitErrorEvent(Zig::GlobalObject* global, EncodedJSValu
|
||||
_linkedBinding Process_stubEmptyFunction Function 0
|
||||
_preload_modules Process_stubEmptyArray PropertyCallback
|
||||
_rawDebug Process_stubEmptyFunction Function 0
|
||||
_startProfilerIdleNotifier Process_stubEmptyFunction Function 0
|
||||
_stopProfilerIdleNotifier Process_stubEmptyFunction Function 0
|
||||
_tickCallback Process_stubEmptyFunction Function 0
|
||||
abort Process_functionAbort Function 1
|
||||
allowedNodeEnvironmentFlags Process_stubEmptySet PropertyCallback
|
||||
@@ -3670,7 +3672,93 @@ const JSC::ClassInfo Process::s_info
|
||||
= { "Process"_s, &Base::s_info, &processObjectTable, nullptr,
|
||||
CREATE_METHOD_TABLE(Process) };
|
||||
|
||||
void Process::finishCreation(JSC::VM& vm)
|
||||
#if OS(WINDOWS)
|
||||
#define FOR_EACH_UNSUPPORTED_WORKER_POSIX_FUNCTION(V)
|
||||
#else
|
||||
#define FOR_EACH_UNSUPPORTED_WORKER_POSIX_FUNCTION(V) \
|
||||
V(setuid) \
|
||||
V(seteuid) \
|
||||
V(setgid) \
|
||||
V(setegid) \
|
||||
V(setgroups) \
|
||||
V(initgroups)
|
||||
#endif
|
||||
|
||||
#define FOR_EACH_UNSUPPORTED_WORKER_FUNCTION(V) \
|
||||
V(abort) \
|
||||
V(chdir) \
|
||||
V(send) \
|
||||
V(disconnect) \
|
||||
FOR_EACH_UNSUPPORTED_WORKER_POSIX_FUNCTION(V)
|
||||
|
||||
#define DEFINE_DISABLED_FUNCTION(ident) \
|
||||
JSC_DEFINE_HOST_FUNCTION(processDisabledFunction_##ident, (JSC::JSGlobalObject * globalObject, JSC::CallFrame * callFrame)) \
|
||||
{ \
|
||||
static constexpr char msg[] = "process." #ident "()"; \
|
||||
static constexpr ASCIILiteral msg_s { msg }; \
|
||||
auto& vm = JSC::getVM(globalObject); \
|
||||
auto scope = DECLARE_THROW_SCOPE(vm); \
|
||||
return Bun::ERR::WORKER_UNSUPPORTED_OPERATION(scope, globalObject, msg_s); \
|
||||
}
|
||||
|
||||
FOR_EACH_UNSUPPORTED_WORKER_FUNCTION(DEFINE_DISABLED_FUNCTION)
|
||||
|
||||
#undef DEFINE_DISABLED_FUNCTION
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(processDisabledGetter_channel, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
return Bun::ERR::WORKER_UNSUPPORTED_OPERATION(scope, globalObject, "process.channel"_s);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(processDisabledGetter_connected, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
return Bun::ERR::WORKER_UNSUPPORTED_OPERATION(scope, globalObject, "process.connected"_s);
|
||||
}
|
||||
|
||||
void Process::installDisabledFunctions(JSC::VM& vm, Zig::GlobalObject* globalObject)
|
||||
{
|
||||
#define ASSIGN_DISABLED_FUNCTION(ident) \
|
||||
{ \
|
||||
auto* fn = JSFunction::create(vm, globalObject, 0, "unavailableInWorker"_s, processDisabledFunction_##ident, ImplementationVisibility::Public); \
|
||||
fn->putDirect(vm, Identifier::fromString(vm, "disabled"_s), jsBoolean(true)); \
|
||||
putDirect(vm, Identifier::fromString(vm, #ident##_s), fn); \
|
||||
}
|
||||
|
||||
// set up the functions that are supposed to throw and have `disabled` set to true
|
||||
FOR_EACH_UNSUPPORTED_WORKER_FUNCTION(ASSIGN_DISABLED_FUNCTION)
|
||||
|
||||
#undef ASSIGN_DISABLED_FUNCTION
|
||||
|
||||
// if we have IPC, set up getters for these properties that throw
|
||||
auto fdValue = globalObject->processEnvObject()->get(globalObject, Identifier::fromString(vm, "NODE_CHANNEL_FD"_s));
|
||||
if (fdValue.toBoolean(globalObject)) {
|
||||
putDirectAccessor(
|
||||
globalObject,
|
||||
Identifier::fromString(vm, "channel"_s),
|
||||
GetterSetter::create(
|
||||
vm,
|
||||
globalObject,
|
||||
JSFunction::create(vm, globalObject, 0, "unavailableInWorker"_s, processDisabledGetter_channel, ImplementationVisibility::Public),
|
||||
jsUndefined()),
|
||||
PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::Accessor);
|
||||
|
||||
putDirectAccessor(
|
||||
globalObject,
|
||||
Identifier::fromString(vm, "connected"_s),
|
||||
GetterSetter::create(
|
||||
vm,
|
||||
globalObject,
|
||||
JSFunction::create(vm, globalObject, 0, "unavailableInWorker"_s, processDisabledGetter_connected, ImplementationVisibility::Public),
|
||||
jsUndefined()),
|
||||
PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::Accessor);
|
||||
}
|
||||
}
|
||||
|
||||
void Process::finishCreation(JSC::VM& vm, Zig::GlobalObject& globalObject)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
|
||||
@@ -3700,6 +3788,18 @@ void Process::finishCreation(JSC::VM& vm)
|
||||
|
||||
putDirect(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, String("process"_s)), 0);
|
||||
putDirect(vm, Identifier::fromString(vm, "_exiting"_s), jsBoolean(false), 0);
|
||||
|
||||
if (globalObject.worker()) {
|
||||
installDisabledFunctions(vm, &globalObject);
|
||||
} else {
|
||||
// these properties need to not even exist in workers, since node tests that `prop in process` is false
|
||||
auto* fn = JSFunction::create(vm, &globalObject, 0, "(anonymous)"_s, Process_stubEmptyFunction, ImplementationVisibility::Public);
|
||||
putDirect(vm, Identifier::fromString(vm, "_startProfilerIdleNotifier"_s), fn);
|
||||
putDirect(vm, Identifier::fromString(vm, "_stopProfilerIdleNotifier"_s), fn);
|
||||
putDirect(vm, Identifier::fromString(vm, "_debugProcess"_s), fn);
|
||||
putDirect(vm, Identifier::fromString(vm, "_debugPause"_s), fn);
|
||||
putDirect(vm, Identifier::fromString(vm, "_debugEnd"_s), fn);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
@@ -84,12 +84,12 @@ public:
|
||||
JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
|
||||
}
|
||||
|
||||
static Process* create(WebCore::JSDOMGlobalObject& globalObject, JSC::Structure* structure)
|
||||
static Process* create(Zig::GlobalObject& globalObject, JSC::Structure* structure)
|
||||
{
|
||||
auto emitter = WebCore::EventEmitter::create(*globalObject.scriptExecutionContext());
|
||||
Process* accessor = new (NotNull, JSC::allocateCell<Process>(globalObject.vm())) Process(structure, globalObject, WTFMove(emitter));
|
||||
accessor->finishCreation(globalObject.vm());
|
||||
return accessor;
|
||||
Process* process = new (NotNull, JSC::allocateCell<Process>(globalObject.vm())) Process(structure, globalObject, WTFMove(emitter));
|
||||
process->finishCreation(globalObject.vm(), globalObject);
|
||||
return process;
|
||||
}
|
||||
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
@@ -107,8 +107,6 @@ public:
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForProcessObject = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
void finishCreation(JSC::VM& vm);
|
||||
|
||||
inline void setUncaughtExceptionCaptureCallback(JSC::JSValue callback)
|
||||
{
|
||||
m_uncaughtExceptionCaptureCallback.set(vm(), this, callback);
|
||||
@@ -124,6 +122,12 @@ public:
|
||||
inline Structure* memoryUsageStructure() { return m_memoryUsageStructure.getInitializedOnMainThread(this); }
|
||||
inline JSObject* bindingUV() { return m_bindingUV.getInitializedOnMainThread(this); }
|
||||
inline JSObject* bindingNatives() { return m_bindingNatives.getInitializedOnMainThread(this); }
|
||||
|
||||
private:
|
||||
void finishCreation(JSC::VM& vm, Zig::GlobalObject& globalObject);
|
||||
// Replace functions that should be disabled in Workers with stubs that throw an error when
|
||||
// called and have the `disabled` property set to true
|
||||
void installDisabledFunctions(JSC::VM& vm, Zig::GlobalObject* globalObject);
|
||||
};
|
||||
|
||||
bool isSignalName(WTF::String input);
|
||||
|
||||
@@ -999,6 +999,13 @@ JSC::EncodedJSValue INVALID_FILE_URL_PATH(JSC::ThrowScope& throwScope, JSC::JSGl
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue WORKER_UNSUPPORTED_OPERATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral operation)
|
||||
{
|
||||
auto message = makeString(operation, " is not supported in workers"_s);
|
||||
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_WORKER_UNSUPPORTED_OPERATION, message));
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::StringView encoding)
|
||||
{
|
||||
auto message = makeString("Unknown encoding: "_s, encoding);
|
||||
|
||||
@@ -145,6 +145,11 @@ JSC::EncodedJSValue INVALID_FILE_URL_HOST(JSC::ThrowScope& throwScope, JSC::JSGl
|
||||
/// `File URL path {suffix}`
|
||||
JSC::EncodedJSValue INVALID_FILE_URL_PATH(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral suffix);
|
||||
|
||||
// Worker
|
||||
|
||||
// `{operation} is not supported in workers`
|
||||
JSC::EncodedJSValue WORKER_UNSUPPORTED_OPERATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const ASCIILiteral operation);
|
||||
|
||||
}
|
||||
|
||||
void throwBoringSSLError(JSC::VM& vm, JSC::ThrowScope& scope, JSGlobalObject* globalObject, int errorCode);
|
||||
|
||||
@@ -252,6 +252,7 @@ const errors: ErrorCodeMapping = [
|
||||
["ERR_VM_MODULE_LINK_FAILURE", Error],
|
||||
["ERR_WASI_NOT_STARTED", Error],
|
||||
["ERR_WORKER_INIT_FAILED", Error],
|
||||
["ERR_WORKER_UNSUPPORTED_OPERATION", TypeError],
|
||||
["ERR_ZLIB_INITIALIZATION_FAILED", Error],
|
||||
["MODULE_NOT_FOUND", Error],
|
||||
["ERR_INTERNAL_ASSERTION", Error],
|
||||
|
||||
@@ -4376,6 +4376,14 @@ bool GlobalObject::hasNapiFinalizers() const
|
||||
|
||||
void GlobalObject::setNodeWorkerEnvironmentData(JSMap* data) { m_nodeWorkerEnvironmentData.set(vm(), this, data); }
|
||||
|
||||
extern "C" WebCore::Worker* Bun__VirtualMachine__getWorker(VirtualMachine* bunVM);
|
||||
|
||||
WebCore::Worker* GlobalObject::worker()
|
||||
{
|
||||
// TODO make bunVM typed instead of void* everywhere
|
||||
return Bun__VirtualMachine__getWorker(reinterpret_cast<VirtualMachine*>(bunVM()));
|
||||
}
|
||||
|
||||
extern "C" void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
|
||||
@@ -25,6 +25,7 @@ class WorkerGlobalScope;
|
||||
class SubtleCrypto;
|
||||
class EventTarget;
|
||||
class Performance;
|
||||
class Worker;
|
||||
} // namespace WebCore
|
||||
|
||||
namespace Bun {
|
||||
@@ -388,6 +389,10 @@ public:
|
||||
return func;
|
||||
}
|
||||
|
||||
// Return the Worker object if this global object is running in a worker, or nullptr
|
||||
// if it is not.
|
||||
WebCore::Worker* worker();
|
||||
|
||||
bool asyncHooksNeedsCleanup = false;
|
||||
double INSPECT_MAX_BYTES = 50;
|
||||
bool isInsideErrorPrepareStackTraceCallback = false;
|
||||
|
||||
@@ -514,8 +514,6 @@ extern "C" void WebWorker__dispatchError(Zig::GlobalObject* globalObject, Worker
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" WebCore::Worker* WebWorker__getParentWorker(void* bunVM);
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsReceiveMessageOnPort, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
@@ -551,7 +549,7 @@ JSValue createNodeWorkerThreadsBinding(Zig::GlobalObject* globalObject)
|
||||
JSValue threadId = jsNumber(0);
|
||||
JSMap* environmentData = nullptr;
|
||||
|
||||
if (auto* worker = WebWorker__getParentWorker(globalObject->bunVM())) {
|
||||
if (auto* worker = globalObject->worker()) {
|
||||
auto& options = worker->options();
|
||||
auto ports = MessagePort::entanglePorts(*ScriptExecutionContext::getScriptExecutionContext(worker->clientIdentifier()), WTFMove(options.dataMessagePorts));
|
||||
RefPtr<WebCore::SerializedScriptValue> serialized = WTFMove(options.workerDataAndEnvironmentData);
|
||||
@@ -597,7 +595,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPostMessage,
|
||||
if (UNLIKELY(!globalObject))
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
Worker* worker = WebWorker__getParentWorker(globalObject->bunVM());
|
||||
Worker* worker = globalObject->worker();
|
||||
if (worker == nullptr)
|
||||
return JSValue::encode(jsUndefined());
|
||||
|
||||
|
||||
@@ -57,11 +57,6 @@ extern fn WebWorker__dispatchOnline(cpp_worker: *anyopaque, *jsc.JSGlobalObject)
|
||||
extern fn WebWorker__fireEarlyMessages(cpp_worker: *anyopaque, *jsc.JSGlobalObject) void;
|
||||
extern fn WebWorker__dispatchError(*jsc.JSGlobalObject, *anyopaque, bun.String, JSValue) void;
|
||||
|
||||
export fn WebWorker__getParentWorker(vm: *jsc.VirtualMachine) ?*anyopaque {
|
||||
const worker = vm.worker orelse return null;
|
||||
return worker.cpp_worker;
|
||||
}
|
||||
|
||||
pub fn hasRequestedTerminate(this: *const WebWorker) bool {
|
||||
return this.requested_terminate.load(.monotonic);
|
||||
}
|
||||
|
||||
70
test/js/node/test/parallel/test-worker-unsupported-things.js
Normal file
70
test/js/node/test/parallel/test-worker-unsupported-things.js
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const { Worker, parentPort } = require('worker_threads');
|
||||
|
||||
// Do not use isMainThread so that this test itself can be run inside a Worker.
|
||||
if (!process.env.HAS_STARTED_WORKER) {
|
||||
process.env.HAS_STARTED_WORKER = 1;
|
||||
process.env.NODE_CHANNEL_FD = 'foo'; // Make worker think it has IPC.
|
||||
const w = new Worker(__filename);
|
||||
w.on('message', common.mustCall((message) => {
|
||||
assert.strictEqual(message, true);
|
||||
}));
|
||||
} else {
|
||||
{
|
||||
const before = process.title;
|
||||
const after = before + ' in worker';
|
||||
process.title = after;
|
||||
assert.strictEqual(process.title, after);
|
||||
}
|
||||
|
||||
{
|
||||
const before = process.debugPort;
|
||||
const after = before + 1;
|
||||
process.debugPort = after;
|
||||
assert.strictEqual(process.debugPort, after);
|
||||
}
|
||||
|
||||
{
|
||||
const mask = 0o600;
|
||||
assert.throws(() => { process.umask(mask); }, {
|
||||
code: 'ERR_WORKER_UNSUPPORTED_OPERATION',
|
||||
message: 'Setting process.umask() is not supported in workers'
|
||||
});
|
||||
}
|
||||
|
||||
const stubs = ['abort', 'chdir', 'send', 'disconnect'];
|
||||
|
||||
if (!common.isWindows) {
|
||||
stubs.push('setuid', 'seteuid', 'setgid',
|
||||
'setegid', 'setgroups', 'initgroups');
|
||||
}
|
||||
|
||||
stubs.forEach((fn) => {
|
||||
assert.strictEqual(process[fn].disabled, true);
|
||||
assert.throws(() => {
|
||||
process[fn]();
|
||||
}, {
|
||||
code: 'ERR_WORKER_UNSUPPORTED_OPERATION',
|
||||
message: `process.${fn}() is not supported in workers`
|
||||
});
|
||||
});
|
||||
|
||||
['channel', 'connected'].forEach((fn) => {
|
||||
assert.throws(() => {
|
||||
process[fn]; // eslint-disable-line no-unused-expressions
|
||||
}, {
|
||||
code: 'ERR_WORKER_UNSUPPORTED_OPERATION',
|
||||
message: `process.${fn} is not supported in workers`
|
||||
});
|
||||
});
|
||||
|
||||
assert.strictEqual('_startProfilerIdleNotifier' in process, false);
|
||||
assert.strictEqual('_stopProfilerIdleNotifier' in process, false);
|
||||
assert.strictEqual('_debugProcess' in process, false);
|
||||
assert.strictEqual('_debugPause' in process, false);
|
||||
assert.strictEqual('_debugEnd' in process, false);
|
||||
|
||||
parentPort.postMessage(true);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
import { bunEnv, bunExe, isWindows } from "harness";
|
||||
import { once } from "node:events";
|
||||
import fs from "node:fs";
|
||||
import { join, relative, resolve } from "node:path";
|
||||
@@ -402,10 +402,12 @@ describe("environmentData", () => {
|
||||
|
||||
describe("error event", () => {
|
||||
test("is fired with a copy of the error value", async () => {
|
||||
const worker = new Worker("throw new TypeError('oh no')", { eval: true });
|
||||
const worker = new Worker("const e = new TypeError('oh no'); e.code = 'ERR_OHNO'; throw e;", { eval: true });
|
||||
const [err] = await once(worker, "error");
|
||||
expect(err).toBeInstanceOf(TypeError);
|
||||
expect(err.message).toBe("oh no");
|
||||
// TODO(@190n) find out why extra properties on errors are not propagated
|
||||
// expect(err.code).toBe("ERR_OHNO");
|
||||
});
|
||||
|
||||
test("falls back to string when the error cannot be serialized", async () => {
|
||||
@@ -421,3 +423,106 @@ describe("error event", () => {
|
||||
expect(err.message).toMatch(/MessagePort \{.*\}/s);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unsupported functions and properties", () => {
|
||||
test("getting process.umask works", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const worker = new Worker("process.exit(process.umask())", { eval: true });
|
||||
worker.on("error", reject);
|
||||
worker.on("exit", resolve);
|
||||
expect(await promise).toBe(process.umask());
|
||||
});
|
||||
|
||||
test("setting process.umask", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const worker = new Worker(
|
||||
/* js */ `
|
||||
import assert from "node:assert";
|
||||
assert.throws(() => process.umask(123), {
|
||||
name: "TypeError",
|
||||
code: "ERR_WORKER_UNSUPPORTED_OPERATION",
|
||||
message: "Setting process.umask() is not supported in workers",
|
||||
});
|
||||
`,
|
||||
{
|
||||
eval: true,
|
||||
},
|
||||
);
|
||||
worker.on("error", reject);
|
||||
worker.on("exit", resolve);
|
||||
expect(await promise).toBe(0);
|
||||
});
|
||||
|
||||
test("functions that throw", async () => {
|
||||
const stubs = ["abort", "chdir", "send", "disconnect"];
|
||||
if (!isWindows) {
|
||||
stubs.push("setuid", "seteuid", "setgid", "setegid", "setgroups", "initgroups");
|
||||
}
|
||||
|
||||
let code = 'import assert from "node:assert"';
|
||||
for (const fn of stubs) {
|
||||
code += /* js */ `
|
||||
assert.strictEqual(process.${fn}.disabled, true);
|
||||
assert.strictEqual(process.${fn}.name, "unavailableInWorker");
|
||||
assert.throws(process.${fn}, {
|
||||
name: "TypeError",
|
||||
code: "ERR_WORKER_UNSUPPORTED_OPERATION",
|
||||
message: "process.${fn}() is not supported in workers",
|
||||
});`;
|
||||
}
|
||||
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const worker = new Worker(code, { eval: true });
|
||||
worker.on("error", reject);
|
||||
worker.on("exit", resolve);
|
||||
expect(await promise).toBe(0);
|
||||
});
|
||||
|
||||
test("getters that throw when IPC appears to be enabled", async () => {
|
||||
const before = process.env.NODE_CHANNEL_FD;
|
||||
try {
|
||||
// make worker think we have IPC
|
||||
process.env.NODE_CHANNEL_FD = "truthy";
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const worker = new Worker(
|
||||
/* js */ `
|
||||
import assert from "node:assert";
|
||||
assert.throws(() => process.channel, {
|
||||
name: "TypeError",
|
||||
code: "ERR_WORKER_UNSUPPORTED_OPERATION",
|
||||
message: "process.channel is not supported in workers",
|
||||
});
|
||||
assert.throws(() => process.connected, {
|
||||
name: "TypeError",
|
||||
code: "ERR_WORKER_UNSUPPORTED_OPERATION",
|
||||
message: "process.connected is not supported in workers",
|
||||
});
|
||||
`,
|
||||
{ eval: true },
|
||||
);
|
||||
worker.on("error", reject);
|
||||
worker.on("exit", resolve);
|
||||
expect(await promise).toBe(0);
|
||||
} finally {
|
||||
process.env.NODE_CHANNEL_FD = before;
|
||||
}
|
||||
});
|
||||
|
||||
test("properties that are not defined", async () => {
|
||||
const { promise, resolve, reject } = Promise.withResolvers();
|
||||
const worker = new Worker(
|
||||
/* js */ `
|
||||
import assert from "node:assert";
|
||||
assert.strictEqual("_startProfilerIdleNotifier" in process, false);
|
||||
assert.strictEqual("_stopProfilerIdleNotifier" in process, false);
|
||||
assert.strictEqual("_debugProcess" in process, false);
|
||||
assert.strictEqual("_debugPause" in process, false);
|
||||
assert.strictEqual("_debugEnd" in process, false);
|
||||
`,
|
||||
{ eval: true },
|
||||
);
|
||||
worker.on("error", reject);
|
||||
worker.on("exit", resolve);
|
||||
expect(await promise).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user