Skip to content

Commit 6390f70

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 afbaf92 commit 6390f70

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
@@ -525,4 +525,16 @@ export default [
525525
],
526526
},
527527
},
528+
{
529+
files: [
530+
'lib/internal/per_context/domexception.js',
531+
],
532+
languageOptions: {
533+
globals: {
534+
// Parameters passed to internal modules.
535+
privateSymbols: 'readonly',
536+
perIsolateSymbols: 'readonly',
537+
},
538+
},
539+
},
528540
];

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
@@ -787,13 +787,13 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
787787
Context::Scope context_scope(context);
788788

789789
Local<ObjectTemplate> private_symbols = ObjectTemplate::New(isolate);
790-
Local<Object> private_symbols_object;
791790
#define V(PropertyName, _) \
792791
private_symbols->Set(isolate, #PropertyName, isolate_data->PropertyName());
793792

794793
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
795794
#undef V
796795

796+
Local<Object> private_symbols_object;
797797
if (!private_symbols->NewInstance(context).ToLocal(&private_symbols_object) ||
798798
private_symbols_object->SetPrototypeV2(context, Null(isolate))
799799
.IsNothing()) {
@@ -803,6 +803,32 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
803803
return scope.Escape(private_symbols_object);
804804
}
805805

806+
MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
807+
IsolateData* isolate_data) {
808+
CHECK(isolate_data);
809+
Isolate* isolate = context->GetIsolate();
810+
EscapableHandleScope scope(isolate);
811+
Context::Scope context_scope(context);
812+
813+
Local<ObjectTemplate> per_isolate_symbols = ObjectTemplate::New(isolate);
814+
#define V(PropertyName, _) \
815+
per_isolate_symbols->Set( \
816+
isolate, #PropertyName, isolate_data->PropertyName());
817+
818+
PER_ISOLATE_SYMBOL_PROPERTIES(V)
819+
#undef V
820+
821+
Local<Object> per_isolate_symbols_object;
822+
if (!per_isolate_symbols->NewInstance(context).ToLocal(
823+
&per_isolate_symbols_object) ||
824+
per_isolate_symbols_object->SetPrototypeV2(context, Null(isolate))
825+
.IsNothing()) {
826+
return MaybeLocal<Object>();
827+
}
828+
829+
return scope.Escape(per_isolate_symbols_object);
830+
}
831+
806832
Maybe<void> InitializePrimordials(Local<Context> context,
807833
IsolateData* isolate_data) {
808834
// Run per-context JS files.
@@ -832,6 +858,12 @@ Maybe<void> InitializePrimordials(Local<Context> context,
832858
return Nothing<void>();
833859
}
834860

861+
Local<Object> per_isolate_symbols;
862+
if (!InitializePerIsolateSymbols(context, isolate_data)
863+
.ToLocal(&per_isolate_symbols)) {
864+
return Nothing<void>();
865+
}
866+
835867
static const char* context_files[] = {"internal/per_context/primordials",
836868
"internal/per_context/domexception",
837869
"internal/per_context/messageport",
@@ -847,7 +879,8 @@ Maybe<void> InitializePrimordials(Local<Context> context,
847879
builtin_loader.SetEagerCompile();
848880

849881
for (const char** module = context_files; *module != nullptr; module++) {
850-
Local<Value> arguments[] = {exports, primordials, private_symbols};
882+
Local<Value> arguments[] = {
883+
exports, primordials, private_symbols, per_isolate_symbols};
851884

852885
if (builtin_loader
853886
.CompileAndCall(

src/node_builtins.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
415415
FIXED_ONE_BYTE_STRING(isolate, "exports"),
416416
FIXED_ONE_BYTE_STRING(isolate, "primordials"),
417417
FIXED_ONE_BYTE_STRING(isolate, "privateSymbols"),
418+
FIXED_ONE_BYTE_STRING(isolate, "perIsolateSymbols"),
418419
};
419420
} else if (strncmp(id, "internal/main/", strlen("internal/main/")) == 0 ||
420421
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)