Skip to content

Commit 914701d

Browse files
legendecasjazelly
authored andcommitted
lib,src: support DOMException ser-des
Added serialization and deserialization support for `DOMException`. Co-Authored-By: jazelly <xzha4350@gmail.com> PR-URL: #58649 Fixes: #49181 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Jason Zhang <xzha4350@gmail.com>
1 parent 4baf216 commit 914701d

File tree

6 files changed

+137
-2
lines changed

6 files changed

+137
-2
lines changed

lib/eslint.config_partial.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,4 +510,16 @@ export default [
510510
],
511511
},
512512
},
513+
{
514+
files: [
515+
'lib/internal/per_context/domexception.js',
516+
],
517+
languageOptions: {
518+
globals: {
519+
// Parameters passed to internal modules.
520+
privateSymbols: 'readonly',
521+
perIsolateSymbols: 'readonly',
522+
},
523+
},
524+
},
513525
];

lib/internal/per_context/domexception.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ const {
1212
SymbolToStringTag,
1313
TypeError,
1414
} = primordials;
15+
const {
16+
transfer_mode_private_symbol,
17+
} = privateSymbols;
18+
const {
19+
messaging_clone_symbol,
20+
messaging_deserialize_symbol,
21+
} = perIsolateSymbols;
22+
23+
/**
24+
* Maps to BaseObject::TransferMode::kCloneable
25+
*/
26+
const kCloneable = 2;
1527

1628
function throwInvalidThisError(Base, type) {
1729
const err = new Base();
@@ -50,6 +62,7 @@ const disusedNamesSet = new SafeSet()
5062

5163
class DOMException {
5264
constructor(message = '', options = 'Error') {
65+
this[transfer_mode_private_symbol] = kCloneable;
5366
ErrorCaptureStackTrace(this);
5467

5568
if (options && typeof options === 'object') {
@@ -76,6 +89,28 @@ class DOMException {
7689
}
7790
}
7891

92+
[messaging_clone_symbol]() {
93+
// See serialization steps in https://webidl.spec.whatwg.org/#dom-domexception-domexception
94+
const internals = internalsMap.get(this);
95+
return {
96+
data: {
97+
message: internals.message,
98+
name: internals.name,
99+
stack: this.stack,
100+
},
101+
deserializeInfo: 'internal/worker/clone_dom_exception:DOMException',
102+
};
103+
}
104+
105+
[messaging_deserialize_symbol](data) {
106+
// See deserialization steps in https://webidl.spec.whatwg.org/#dom-domexception-domexception
107+
internalsMap.set(this, {
108+
message: data.message,
109+
name: data.name,
110+
});
111+
this.stack = data.stack;
112+
}
113+
79114
get name() {
80115
const internals = internalsMap.get(this);
81116
if (internals === undefined) {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict';
2+
3+
// Delegate to the actual DOMException implementation.
4+
module.exports = {
5+
DOMException: internalBinding('messaging').DOMException,
6+
};

src/api/environment.cc

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,13 +778,13 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
778778
Context::Scope context_scope(context);
779779

780780
Local<ObjectTemplate> private_symbols = ObjectTemplate::New(isolate);
781-
Local<Object> private_symbols_object;
782781
#define V(PropertyName, _) \
783782
private_symbols->Set(isolate, #PropertyName, isolate_data->PropertyName());
784783

785784
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
786785
#undef V
787786

787+
Local<Object> private_symbols_object;
788788
if (!private_symbols->NewInstance(context).ToLocal(&private_symbols_object) ||
789789
private_symbols_object->SetPrototype(context, Null(isolate))
790790
.IsNothing()) {
@@ -794,6 +794,32 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
794794
return scope.Escape(private_symbols_object);
795795
}
796796

797+
MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
798+
IsolateData* isolate_data) {
799+
CHECK(isolate_data);
800+
Isolate* isolate = context->GetIsolate();
801+
EscapableHandleScope scope(isolate);
802+
Context::Scope context_scope(context);
803+
804+
Local<ObjectTemplate> per_isolate_symbols = ObjectTemplate::New(isolate);
805+
#define V(PropertyName, _) \
806+
per_isolate_symbols->Set( \
807+
isolate, #PropertyName, isolate_data->PropertyName());
808+
809+
PER_ISOLATE_SYMBOL_PROPERTIES(V)
810+
#undef V
811+
812+
Local<Object> per_isolate_symbols_object;
813+
if (!per_isolate_symbols->NewInstance(context).ToLocal(
814+
&per_isolate_symbols_object) ||
815+
per_isolate_symbols_object->SetPrototype(context, Null(isolate))
816+
.IsNothing()) {
817+
return MaybeLocal<Object>();
818+
}
819+
820+
return scope.Escape(per_isolate_symbols_object);
821+
}
822+
797823
Maybe<void> InitializePrimordials(Local<Context> context,
798824
IsolateData* isolate_data) {
799825
// Run per-context JS files.
@@ -822,6 +848,12 @@ Maybe<void> InitializePrimordials(Local<Context> context,
822848
return Nothing<void>();
823849
}
824850

851+
Local<Object> per_isolate_symbols;
852+
if (!InitializePerIsolateSymbols(context, isolate_data)
853+
.ToLocal(&per_isolate_symbols)) {
854+
return Nothing<void>();
855+
}
856+
825857
static const char* context_files[] = {"internal/per_context/primordials",
826858
"internal/per_context/domexception",
827859
"internal/per_context/messageport",
@@ -837,7 +869,8 @@ Maybe<void> InitializePrimordials(Local<Context> context,
837869
builtin_loader.SetEagerCompile();
838870

839871
for (const char** module = context_files; *module != nullptr; module++) {
840-
Local<Value> arguments[] = {exports, primordials, private_symbols};
872+
Local<Value> arguments[] = {
873+
exports, primordials, private_symbols, per_isolate_symbols};
841874

842875
if (builtin_loader
843876
.CompileAndCall(

src/node_builtins.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
416416
FIXED_ONE_BYTE_STRING(isolate, "exports"),
417417
FIXED_ONE_BYTE_STRING(isolate, "primordials"),
418418
FIXED_ONE_BYTE_STRING(isolate, "privateSymbols"),
419+
FIXED_ONE_BYTE_STRING(isolate, "perIsolateSymbols"),
419420
};
420421
} else if (strncmp(id, "internal/main/", strlen("internal/main/")) == 0 ||
421422
strncmp(id,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
require('../common');
4+
const assert = require('assert');
5+
6+
function assertDOMException(actual, expected) {
7+
assert.strictEqual(actual instanceof DOMException, true);
8+
assert.strictEqual(actual.message, expected.message);
9+
assert.strictEqual(actual.name, expected.name);
10+
assert.strictEqual(actual.code, expected.code);
11+
assert.strictEqual(actual.stack, expected.stack);
12+
}
13+
14+
{
15+
// Clone basic DOMException
16+
const e = new DOMException('test');
17+
const clone = structuredClone(e);
18+
const clone2 = structuredClone(clone);
19+
assertDOMException(clone, e);
20+
assertDOMException(clone2, e);
21+
}
22+
23+
{
24+
// Clone a DOMException with a name
25+
const e = new DOMException('test', 'DataCloneError');
26+
const clone = structuredClone(e);
27+
const clone2 = structuredClone(clone);
28+
assertDOMException(clone, e);
29+
assertDOMException(clone2, e);
30+
}
31+
32+
{
33+
// Clone an arbitrary object with a DOMException prototype
34+
const obj = {};
35+
Object.setPrototypeOf(obj, DOMException.prototype);
36+
const clone = structuredClone(obj);
37+
assert.strictEqual(clone instanceof DOMException, false);
38+
}
39+
40+
{
41+
// Transfer a DOMException. DOMExceptions are not transferable.
42+
const e = new DOMException('test');
43+
assert.throws(() => {
44+
structuredClone(e, { transfer: [e] });
45+
}, {
46+
name: 'DataCloneError',
47+
});
48+
}

0 commit comments

Comments
 (0)