Skip to content

lib: make domexception a native error #58691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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
2 changes: 1 addition & 1 deletion common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

# Reset this number to 0 on major V8 upgrades.
# Increment by one for each non-official patch applied to deps/v8.
'v8_embedder_string': '-node.13',
'v8_embedder_string': '-node.14',

##### V8 defaults for Node.js #####

Expand Down
13 changes: 11 additions & 2 deletions deps/v8/src/objects/value-serializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,17 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(
case JS_DATA_VIEW_TYPE:
case JS_RAB_GSAB_DATA_VIEW_TYPE:
return WriteJSArrayBufferView(Cast<JSArrayBufferView>(*receiver));
case JS_ERROR_TYPE:
return WriteJSError(Cast<JSObject>(receiver));
case JS_ERROR_TYPE: {
DirectHandle<JSObject> js_error = Cast<JSObject>(receiver);
Maybe<bool> is_host_object = IsHostObject(js_error);
if (is_host_object.IsNothing()) {
return is_host_object;
}
if (is_host_object.FromJust()) {
return WriteHostObject(js_error);
}
return WriteJSError(js_error);
}
case JS_SHARED_ARRAY_TYPE:
return WriteJSSharedArray(Cast<JSSharedArray>(receiver));
case JS_SHARED_STRUCT_TYPE:
Expand Down
63 changes: 63 additions & 0 deletions deps/v8/test/unittests/objects/value-serializer-unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3141,6 +3141,69 @@ TEST_F(ValueSerializerTestWithHostObject, RoundTripHostJSObject) {
ExpectScriptTrue("result.a === result.b");
}

TEST_F(ValueSerializerTestWithHostObject, RoundTripJSErrorObject) {
i::DisableHandleChecksForMockingScope mocking_scope;

EXPECT_CALL(serializer_delegate_, HasCustomHostObject(isolate()))
.WillOnce(Invoke([](Isolate* isolate) { return true; }));
EXPECT_CALL(serializer_delegate_, IsHostObject(isolate(), _))
.WillRepeatedly(Invoke([this](Isolate* isolate, Local<Object> object) {
EXPECT_TRUE(object->IsObject());
Local<Context> context = isolate->GetCurrentContext();
return object->Has(context, StringFromUtf8("my_host_object"));
}));
// Read/Write HostObject methods are not invoked for non-host JSErrors.
EXPECT_CALL(serializer_delegate_, WriteHostObject(isolate(), _)).Times(0);
EXPECT_CALL(deserializer_delegate_, ReadHostObject(isolate())).Times(0);

RoundTripTest(
"var e = new Error('before serialize');"
"({ a: e, get b() { return this.a; } })");
ExpectScriptTrue("!('my_host_object' in result)");
ExpectScriptTrue("!('my_host_object' in result.a)");
ExpectScriptTrue("result.a.message === 'before serialize'");
ExpectScriptTrue("result.a instanceof Error");
ExpectScriptTrue("result.a === result.b");
}

TEST_F(ValueSerializerTestWithHostObject, RoundTripHostJSErrorObject) {
i::DisableHandleChecksForMockingScope mocking_scope;

EXPECT_CALL(serializer_delegate_, HasCustomHostObject(isolate()))
.WillOnce(Invoke([](Isolate* isolate) { return true; }));
EXPECT_CALL(serializer_delegate_, IsHostObject(isolate(), _))
.WillRepeatedly(Invoke([this](Isolate* isolate, Local<Object> object) {
EXPECT_TRUE(object->IsObject());
Local<Context> context = isolate->GetCurrentContext();
return object->Has(context, StringFromUtf8("my_host_object"));
}));
EXPECT_CALL(serializer_delegate_, WriteHostObject(isolate(), _))
.WillOnce(Invoke([this](Isolate*, Local<Object> object) {
EXPECT_TRUE(object->IsObject());
WriteExampleHostObjectTag();
return Just(true);
}));
EXPECT_CALL(deserializer_delegate_, ReadHostObject(isolate()))
.WillOnce(Invoke([this](Isolate* isolate) {
EXPECT_TRUE(ReadExampleHostObjectTag());
Local<Context> context = isolate->GetCurrentContext();
Local<Object> obj =
v8::Exception::Error(StringFromUtf8("deserialized")).As<Object>();
obj->Set(context, StringFromUtf8("my_host_object"), v8::True(isolate))
.Check();
return obj;
}));
RoundTripTest(
"var e = new Error('before serialize');"
"e.my_host_object = true;"
"({ a: e, get b() { return this.a; } })");
ExpectScriptTrue("!('my_host_object' in result)");
ExpectScriptTrue("result.a.my_host_object");
ExpectScriptTrue("result.a.message === 'deserialized'");
ExpectScriptTrue("result.a instanceof Error");
ExpectScriptTrue("result.a === result.b");
}

class ValueSerializerTestWithHostArrayBufferView
: public ValueSerializerTestWithHostObject {
protected:
Expand Down
35 changes: 26 additions & 9 deletions lib/internal/per_context/domexception.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const {
ErrorCaptureStackTrace,
Error,
ErrorPrototype,
ObjectDefineProperties,
ObjectDefineProperty,
Expand Down Expand Up @@ -60,20 +60,33 @@ const disusedNamesSet = new SafeSet()
.add('NoDataAllowedError')
.add('ValidationError');

let DOMExceptionPrototype;
// The DOMException WebIDL interface defines that:
// - ObjectGetPrototypeOf(DOMException) === Function.
// - ObjectGetPrototypeOf(DOMException.prototype) === Error.prototype.
// Thus, we can not simply use the pattern of `class DOMException extends Error` and call
// `super()` to construct an object. The `super` in `super()` call in the constructor will
// be resolved to `Function`, instead of `Error`. Use the trick of return overriding to
// create an object with the `[[ErrorData]]` internal slot.
// Ref: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-getsuperconstructor
class DOMException {
constructor(message = '', options = 'Error') {
this[transfer_mode_private_symbol] = kCloneable;
ErrorCaptureStackTrace(this);
// Invokes the Error constructor to create an object with the [[ErrorData]]
// internal slot.
// eslint-disable-next-line no-restricted-syntax
const self = new Error();
ObjectSetPrototypeOf(self, DOMExceptionPrototype);
self[transfer_mode_private_symbol] = kCloneable;

if (options && typeof options === 'object') {
const { name } = options;
internalsMap.set(this, {
internalsMap.set(self, {
message: `${message}`,
name: `${name}`,
});

if ('cause' in options) {
ObjectDefineProperty(this, 'cause', {
ObjectDefineProperty(self, 'cause', {
__proto__: null,
value: options.cause,
configurable: true,
Expand All @@ -82,11 +95,14 @@ class DOMException {
});
}
} else {
internalsMap.set(this, {
internalsMap.set(self, {
message: `${message}`,
name: `${options}`,
});
}
// Return the error object as the return overriding of the constructor.
// eslint-disable-next-line no-constructor-return
return self;
}

[messaging_clone_symbol]() {
Expand Down Expand Up @@ -142,8 +158,9 @@ class DOMException {
}
}

ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);
ObjectDefineProperties(DOMException.prototype, {
DOMExceptionPrototype = DOMException.prototype;
ObjectSetPrototypeOf(DOMExceptionPrototype, ErrorPrototype);
ObjectDefineProperties(DOMExceptionPrototype, {
[SymbolToStringTag]: { __proto__: null, configurable: true, value: 'DOMException' },
name: { __proto__: null, enumerable: true, configurable: true },
message: { __proto__: null, enumerable: true, configurable: true },
Expand Down Expand Up @@ -181,7 +198,7 @@ for (const { 0: name, 1: codeName, 2: value } of [
]) {
const desc = { enumerable: true, value };
ObjectDefineProperty(DOMException, codeName, desc);
ObjectDefineProperty(DOMException.prototype, codeName, desc);
ObjectDefineProperty(DOMExceptionPrototype, codeName, desc);
nameToCodeMap.set(name, value);
}

Expand Down
11 changes: 1 addition & 10 deletions test/wpt/status/webidl/ecmascript-binding/es-exceptions.json
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
{
"DOMException-is-error.any.js": {
"fail": {
"note": "https://github.com/nodejs/node/issues/56497",
"expected": [
"DOMException-is-error"
]
}
}
}
{}
Loading