Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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.32',
'v8_embedder_string': '-node.33',

##### 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 @@ -643,8 +643,17 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(Handle<JSReceiver> receiver) {
case JS_DATA_VIEW_TYPE:
case JS_RAB_GSAB_DATA_VIEW_TYPE:
return WriteJSArrayBufferView(JSArrayBufferView::cast(*receiver));
case JS_ERROR_TYPE:
return WriteJSError(Handle<JSObject>::cast(receiver));
case JS_ERROR_TYPE: {
Handle<JSObject> js_error = Handle<JSObject>::cast(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(Handle<JSSharedArray>::cast(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 @@ -3139,6 +3139,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
Loading