Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/eslint.config_partial.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -525,4 +525,16 @@ export default [
],
},
},
{
files: [
'lib/internal/per_context/domexception.js',
],
languageOptions: {
globals: {
// Parameters passed to internal modules.
privateSymbols: 'readonly',
perIsolateSymbols: 'readonly',
},
},
},
];
35 changes: 35 additions & 0 deletions lib/internal/per_context/domexception.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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') {
Expand All @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions lib/internal/worker/clone_dom_exception.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use strict';

// Delegate to the actual DOMException implementation.
module.exports = {
DOMException: internalBinding('messaging').DOMException,
};
37 changes: 35 additions & 2 deletions src/api/environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand All @@ -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.
Expand Down Expand Up @@ -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",
Expand All @@ -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(
Expand Down
1 change: 1 addition & 0 deletions src/node_builtins.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 48 additions & 0 deletions test/parallel/test-structuredClone-domexception.js
Original file line number Diff line number Diff line change
@@ -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',
});
}
Loading