Skip to content

Commit 1e669b2

Browse files
addaleaxtargos
authored andcommitted
src,lib: make DOMException available in all Contexts
This allows using `DOMException` from Node.js code for any `vm.Context`. PR-URL: #26497 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent e044563 commit 1e669b2

File tree

9 files changed

+84
-37
lines changed

9 files changed

+84
-37
lines changed

lib/internal/bootstrap/cache.js

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const cannotBeRequired = [
2525
'internal/bootstrap/node',
2626

2727
'internal/per_context/setup',
28+
'internal/per_context/domexception',
2829
];
2930

3031
// Skip modules that cannot be required when they are not

lib/internal/bootstrap/node.js

-9
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,6 @@ if (!config.noBrowserGlobals) {
220220
defineOperation(global, 'setImmediate', timers.setImmediate);
221221
}
222222

223-
setupDOMException();
224-
225223
Object.defineProperty(process, 'argv0', {
226224
enumerable: true,
227225
configurable: false,
@@ -450,13 +448,6 @@ function setupQueueMicrotask() {
450448
});
451449
}
452450

453-
function setupDOMException() {
454-
// Registers the constructor with C++.
455-
const DOMException = NativeModule.require('internal/domexception');
456-
const { registerDOMException } = internalBinding('messaging');
457-
registerDOMException(DOMException);
458-
}
459-
460451
function noop() {}
461452

462453
// https://heycam.github.io/webidl/#es-namespaces

lib/internal/domexception.js lib/internal/per_context/domexception.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
'use strict';
22

3-
const { ERR_INVALID_THIS } = require('internal/errors').codes;
3+
class ERR_INVALID_THIS extends TypeError {
4+
constructor(type) {
5+
super('Value of "this" must be of ' + type);
6+
}
7+
8+
get code() { return 'ERR_INVALID_THIS'; }
9+
}
410

511
const internalsMap = new WeakMap();
612

@@ -83,4 +89,4 @@ for (const [name, codeName, value] of [
8389
nameToCodeMap.set(name, value);
8490
}
8591

86-
module.exports = DOMException;
92+
exports.DOMException = DOMException;

node.gyp

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
'lib/internal/bootstrap/node.js',
3434
'lib/internal/bootstrap/pre_execution.js',
3535
'lib/internal/per_context/setup.js',
36+
'lib/internal/per_context/domexception.js',
3637
'lib/async_hooks.js',
3738
'lib/assert.js',
3839
'lib/buffer.js',
@@ -117,7 +118,6 @@
117118
'lib/internal/dgram.js',
118119
'lib/internal/dns/promises.js',
119120
'lib/internal/dns/utils.js',
120-
'lib/internal/domexception.js',
121121
'lib/internal/encoding.js',
122122
'lib/internal/errors.js',
123123
'lib/internal/error-serdes.js',

src/api/environment.cc

+34-2
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515

1616
namespace node {
1717
using v8::Context;
18+
using v8::EscapableHandleScope;
1819
using v8::Function;
1920
using v8::HandleScope;
2021
using v8::Isolate;
2122
using v8::Local;
2223
using v8::MaybeLocal;
2324
using v8::Message;
2425
using v8::MicrotasksPolicy;
26+
using v8::Object;
2527
using v8::ObjectTemplate;
28+
using v8::Private;
2629
using v8::String;
2730
using v8::Value;
2831

@@ -249,6 +252,26 @@ void FreePlatform(MultiIsolatePlatform* platform) {
249252
delete platform;
250253
}
251254

255+
MaybeLocal<Object> GetPerContextExports(Local<Context> context) {
256+
Isolate* isolate = context->GetIsolate();
257+
EscapableHandleScope handle_scope(isolate);
258+
259+
Local<Object> global = context->Global();
260+
Local<Private> key = Private::ForApi(isolate,
261+
FIXED_ONE_BYTE_STRING(isolate, "node:per_context_binding_exports"));
262+
263+
Local<Value> existing_value;
264+
if (!global->GetPrivate(context, key).ToLocal(&existing_value))
265+
return MaybeLocal<Object>();
266+
if (existing_value->IsObject())
267+
return handle_scope.Escape(existing_value.As<Object>());
268+
269+
Local<Object> exports = Object::New(isolate);
270+
if (context->Global()->SetPrivate(context, key, exports).IsNothing())
271+
return MaybeLocal<Object>();
272+
return handle_scope.Escape(exports);
273+
}
274+
252275
Local<Context> NewContext(Isolate* isolate,
253276
Local<ObjectTemplate> object_template) {
254277
auto context = Context::New(isolate, nullptr, object_template);
@@ -261,16 +284,25 @@ Local<Context> NewContext(Isolate* isolate,
261284
{
262285
// Run per-context JS files.
263286
Context::Scope context_scope(context);
287+
Local<Object> exports;
288+
if (!GetPerContextExports(context).ToLocal(&exports))
289+
return Local<Context>();
290+
291+
Local<String> global_string = FIXED_ONE_BYTE_STRING(isolate, "global");
292+
Local<String> exports_string = FIXED_ONE_BYTE_STRING(isolate, "exports");
264293

265294
static const char* context_files[] = {
266295
"internal/per_context/setup",
296+
"internal/per_context/domexception",
267297
nullptr
268298
};
269299

270300
for (const char** module = context_files; *module != nullptr; module++) {
271301
std::vector<Local<String>> parameters = {
272-
FIXED_ONE_BYTE_STRING(isolate, "global")};
273-
Local<Value> arguments[] = {context->Global()};
302+
global_string,
303+
exports_string
304+
};
305+
Local<Value> arguments[] = {context->Global(), exports};
274306
MaybeLocal<Function> maybe_fn =
275307
per_process::native_module_loader.LookupAndCompile(
276308
context, *module, &parameters, nullptr);

src/node_internals.h

+2
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ void DefineZlibConstants(v8::Local<v8::Object> target);
294294
v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env);
295295
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
296296
const char* main_script_id);
297+
v8::MaybeLocal<v8::Object> GetPerContextExports(v8::Local<v8::Context> context);
298+
297299
namespace coverage {
298300
bool StartCoverageCollection(Environment* env);
299301
}

src/node_messaging.cc

+22-20
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,30 @@ uint32_t Message::AddWASMModule(WasmCompiledModule::TransferrableModule&& mod) {
177177

178178
namespace {
179179

180-
void ThrowDataCloneException(Environment* env, Local<String> message) {
180+
void ThrowDataCloneException(Local<Context> context, Local<String> message) {
181+
Isolate* isolate = context->GetIsolate();
181182
Local<Value> argv[] = {
182183
message,
183-
FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError")
184+
FIXED_ONE_BYTE_STRING(isolate, "DataCloneError")
184185
};
185186
Local<Value> exception;
186-
Local<Function> domexception_ctor = env->domexception_function();
187-
CHECK(!domexception_ctor.IsEmpty());
188-
if (!domexception_ctor->NewInstance(env->context(), arraysize(argv), argv)
187+
188+
Local<Object> per_context_bindings;
189+
Local<Value> domexception_ctor_val;
190+
if (!GetPerContextExports(context).ToLocal(&per_context_bindings) ||
191+
!per_context_bindings->Get(context,
192+
FIXED_ONE_BYTE_STRING(isolate, "DOMException"))
193+
.ToLocal(&domexception_ctor_val)) {
194+
return;
195+
}
196+
197+
CHECK(domexception_ctor_val->IsFunction());
198+
Local<Function> domexception_ctor = domexception_ctor_val.As<Function>();
199+
if (!domexception_ctor->NewInstance(context, arraysize(argv), argv)
189200
.ToLocal(&exception)) {
190201
return;
191202
}
192-
env->isolate()->ThrowException(exception);
203+
isolate->ThrowException(exception);
193204
}
194205

195206
// This tells V8 how to serialize objects that it does not understand
@@ -201,7 +212,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
201212
: env_(env), context_(context), msg_(m) {}
202213

203214
void ThrowDataCloneError(Local<String> message) override {
204-
ThrowDataCloneException(env_, message);
215+
ThrowDataCloneException(context_, message);
205216
}
206217

207218
Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override {
@@ -309,7 +320,7 @@ Maybe<bool> Message::Serialize(Environment* env,
309320
if (std::find(array_buffers.begin(), array_buffers.end(), ab) !=
310321
array_buffers.end()) {
311322
ThrowDataCloneException(
312-
env,
323+
context,
313324
FIXED_ONE_BYTE_STRING(
314325
env->isolate(),
315326
"Transfer list contains duplicate ArrayBuffer"));
@@ -326,15 +337,15 @@ Maybe<bool> Message::Serialize(Environment* env,
326337
// Check if the source MessagePort is being transferred.
327338
if (!source_port.IsEmpty() && entry == source_port) {
328339
ThrowDataCloneException(
329-
env,
340+
context,
330341
FIXED_ONE_BYTE_STRING(env->isolate(),
331342
"Transfer list contains source port"));
332343
return Nothing<bool>();
333344
}
334345
MessagePort* port = Unwrap<MessagePort>(entry.As<Object>());
335346
if (port == nullptr || port->IsDetached()) {
336347
ThrowDataCloneException(
337-
env,
348+
context,
338349
FIXED_ONE_BYTE_STRING(
339350
env->isolate(),
340351
"MessagePort in transfer list is already detached"));
@@ -343,7 +354,7 @@ Maybe<bool> Message::Serialize(Environment* env,
343354
if (std::find(delegate.ports_.begin(), delegate.ports_.end(), port) !=
344355
delegate.ports_.end()) {
345356
ThrowDataCloneException(
346-
env,
357+
context,
347358
FIXED_ONE_BYTE_STRING(
348359
env->isolate(),
349360
"Transfer list contains duplicate MessagePort"));
@@ -811,13 +822,6 @@ static void MessageChannel(const FunctionCallbackInfo<Value>& args) {
811822
.FromJust();
812823
}
813824

814-
static void RegisterDOMException(const FunctionCallbackInfo<Value>& args) {
815-
Environment* env = Environment::GetCurrent(args);
816-
CHECK_EQ(args.Length(), 1);
817-
CHECK(args[0]->IsFunction());
818-
env->set_domexception_function(args[0].As<Function>());
819-
}
820-
821825
static void InitMessaging(Local<Object> target,
822826
Local<Value> unused,
823827
Local<Context> context,
@@ -839,8 +843,6 @@ static void InitMessaging(Local<Object> target,
839843
GetMessagePortConstructor(env, context).ToLocalChecked())
840844
.FromJust();
841845

842-
env->SetMethod(target, "registerDOMException", RegisterDOMException);
843-
844846
// These are not methods on the MessagePort prototype, because
845847
// the browser equivalents do not provide them.
846848
env->SetMethod(target, "stopMessagePort", MessagePort::Stop);

test/parallel/test-bootstrap-modules.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const expectedModules = new Set([
1717
'Internal Binding credentials',
1818
'Internal Binding fs',
1919
'Internal Binding inspector',
20-
'Internal Binding messaging',
2120
'Internal Binding module_wrap',
2221
'Internal Binding native_module',
2322
'Internal Binding options',
@@ -38,7 +37,6 @@ const expectedModules = new Set([
3837
'NativeModule internal/console/constructor',
3938
'NativeModule internal/console/global',
4039
'NativeModule internal/constants',
41-
'NativeModule internal/domexception',
4240
'NativeModule internal/encoding',
4341
'NativeModule internal/errors',
4442
'NativeModule internal/fixed_queue',
@@ -74,6 +72,7 @@ if (common.isMainThread) {
7472
expectedModules.add('NativeModule internal/process/stdio');
7573
} else {
7674
expectedModules.add('Internal Binding heap_utils');
75+
expectedModules.add('Internal Binding messaging');
7776
expectedModules.add('Internal Binding serdes');
7877
expectedModules.add('Internal Binding stream_wrap');
7978
expectedModules.add('Internal Binding symbols');

test/wpt/test-url.js

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,30 @@
33
// Flags: --expose-internals
44

55
require('../common');
6+
const assert = require('assert');
67
const { WPTRunner } = require('../common/wpt');
78

89
const runner = new WPTRunner('url');
910

1011
// Copy global descriptors from the global object
1112
runner.copyGlobalsFromObject(global, ['URL', 'URLSearchParams']);
1213
// Needed by urlsearchparams-constructor.any.js
14+
let DOMException;
1315
runner.defineGlobal('DOMException', {
1416
get() {
15-
return require('internal/domexception');
17+
// A 'hack' to get the DOMException constructor since we don't have it
18+
// on the global object.
19+
if (DOMException === undefined) {
20+
const port = new (require('worker_threads').MessagePort)();
21+
const ab = new ArrayBuffer(1);
22+
try {
23+
port.postMessage(ab, [ab, ab]);
24+
} catch (err) {
25+
DOMException = err.constructor;
26+
}
27+
assert.strictEqual(DOMException.name, 'DOMException');
28+
}
29+
return DOMException;
1630
}
1731
});
1832

0 commit comments

Comments
 (0)