lib,src: support DOMException ser-des

Added serialization and deserialization support for `DOMException`.

Co-Authored-By: jazelly <xzha4350@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/58649
Fixes: https://github.com/nodejs/node/issues/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>
This commit is contained in:
Chengzhong Wu 2025-06-11 17:11:18 +01:00 committed by GitHub
parent 3a7f8efe60
commit 5457443210
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 137 additions and 2 deletions

View File

@ -525,4 +525,16 @@ export default [
],
},
},
{
files: [
'lib/internal/per_context/domexception.js',
],
languageOptions: {
globals: {
// Parameters passed to internal modules.
privateSymbols: 'readonly',
perIsolateSymbols: 'readonly',
},
},
},
];

View File

@ -12,6 +12,18 @@ const {
SymbolToStringTag,
TypeError,
} = primordials;
const {
transfer_mode_private_symbol,
} = privateSymbols;
const {
messaging_clone_symbol,
messaging_deserialize_symbol,
} = perIsolateSymbols;
/**
* Maps to BaseObject::TransferMode::kCloneable
*/
const kCloneable = 2;
function throwInvalidThisError(Base, type) {
const err = new Base();
@ -50,6 +62,7 @@ const disusedNamesSet = new SafeSet()
class DOMException {
constructor(message = '', options = 'Error') {
this[transfer_mode_private_symbol] = kCloneable;
ErrorCaptureStackTrace(this);
if (options && typeof options === 'object') {
@ -76,6 +89,28 @@ class DOMException {
}
}
[messaging_clone_symbol]() {
// See serialization steps in https://webidl.spec.whatwg.org/#dom-domexception-domexception
const internals = internalsMap.get(this);
return {
data: {
message: internals.message,
name: internals.name,
stack: this.stack,
},
deserializeInfo: 'internal/worker/clone_dom_exception:DOMException',
};
}
[messaging_deserialize_symbol](data) {
// See deserialization steps in https://webidl.spec.whatwg.org/#dom-domexception-domexception
internalsMap.set(this, {
message: data.message,
name: data.name,
});
this.stack = data.stack;
}
get name() {
const internals = internalsMap.get(this);
if (internals === undefined) {

View File

@ -0,0 +1,6 @@
'use strict';
// Delegate to the actual DOMException implementation.
module.exports = {
DOMException: internalBinding('messaging').DOMException,
};

View File

@ -767,13 +767,13 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
Context::Scope context_scope(context);
Local<ObjectTemplate> private_symbols = ObjectTemplate::New(isolate);
Local<Object> private_symbols_object;
#define V(PropertyName, _) \
private_symbols->Set(isolate, #PropertyName, isolate_data->PropertyName());
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
#undef V
Local<Object> private_symbols_object;
if (!private_symbols->NewInstance(context).ToLocal(&private_symbols_object) ||
private_symbols_object->SetPrototypeV2(context, Null(isolate))
.IsNothing()) {
@ -783,6 +783,32 @@ MaybeLocal<Object> InitializePrivateSymbols(Local<Context> context,
return scope.Escape(private_symbols_object);
}
MaybeLocal<Object> InitializePerIsolateSymbols(Local<Context> context,
IsolateData* isolate_data) {
CHECK(isolate_data);
Isolate* isolate = context->GetIsolate();
EscapableHandleScope scope(isolate);
Context::Scope context_scope(context);
Local<ObjectTemplate> per_isolate_symbols = ObjectTemplate::New(isolate);
#define V(PropertyName, _) \
per_isolate_symbols->Set( \
isolate, #PropertyName, isolate_data->PropertyName());
PER_ISOLATE_SYMBOL_PROPERTIES(V)
#undef V
Local<Object> per_isolate_symbols_object;
if (!per_isolate_symbols->NewInstance(context).ToLocal(
&per_isolate_symbols_object) ||
per_isolate_symbols_object->SetPrototypeV2(context, Null(isolate))
.IsNothing()) {
return MaybeLocal<Object>();
}
return scope.Escape(per_isolate_symbols_object);
}
Maybe<void> InitializePrimordials(Local<Context> context,
IsolateData* isolate_data) {
// Run per-context JS files.
@ -812,6 +838,12 @@ Maybe<void> InitializePrimordials(Local<Context> context,
return Nothing<void>();
}
Local<Object> per_isolate_symbols;
if (!InitializePerIsolateSymbols(context, isolate_data)
.ToLocal(&per_isolate_symbols)) {
return Nothing<void>();
}
static const char* context_files[] = {"internal/per_context/primordials",
"internal/per_context/domexception",
"internal/per_context/messageport",
@ -827,7 +859,8 @@ Maybe<void> InitializePrimordials(Local<Context> context,
builtin_loader.SetEagerCompile();
for (const char** module = context_files; *module != nullptr; module++) {
Local<Value> arguments[] = {exports, primordials, private_symbols};
Local<Value> arguments[] = {
exports, primordials, private_symbols, per_isolate_symbols};
if (builtin_loader
.CompileAndCall(

View File

@ -416,6 +416,7 @@ MaybeLocal<Function> BuiltinLoader::LookupAndCompile(Local<Context> context,
FIXED_ONE_BYTE_STRING(isolate, "exports"),
FIXED_ONE_BYTE_STRING(isolate, "primordials"),
FIXED_ONE_BYTE_STRING(isolate, "privateSymbols"),
FIXED_ONE_BYTE_STRING(isolate, "perIsolateSymbols"),
};
} else if (strncmp(id, "internal/main/", strlen("internal/main/")) == 0 ||
strncmp(id,

View File

@ -0,0 +1,48 @@
'use strict';
require('../common');
const assert = require('assert');
function assertDOMException(actual, expected) {
assert.strictEqual(actual instanceof DOMException, true);
assert.strictEqual(actual.message, expected.message);
assert.strictEqual(actual.name, expected.name);
assert.strictEqual(actual.code, expected.code);
assert.strictEqual(actual.stack, expected.stack);
}
{
// Clone basic DOMException
const e = new DOMException('test');
const clone = structuredClone(e);
const clone2 = structuredClone(clone);
assertDOMException(clone, e);
assertDOMException(clone2, e);
}
{
// Clone a DOMException with a name
const e = new DOMException('test', 'DataCloneError');
const clone = structuredClone(e);
const clone2 = structuredClone(clone);
assertDOMException(clone, e);
assertDOMException(clone2, e);
}
{
// Clone an arbitrary object with a DOMException prototype
const obj = {};
Object.setPrototypeOf(obj, DOMException.prototype);
const clone = structuredClone(obj);
assert.strictEqual(clone instanceof DOMException, false);
}
{
// Transfer a DOMException. DOMExceptions are not transferable.
const e = new DOMException('test');
assert.throws(() => {
structuredClone(e, { transfer: [e] });
}, {
name: 'DataCloneError',
});
}