@@ -66,6 +66,10 @@ bool Message::IsCloseMessage() const {
66
66
67
67
namespace {
68
68
69
+ MaybeLocal<Function> GetDOMException (Local<Context> context);
70
+
71
+ static const uint32_t kDOMExceptionTag = 0xD011 ;
72
+
69
73
// This is used to tell V8 how to read transferred host objects, like other
70
74
// `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them.
71
75
class DeserializerDelegate : public ValueDeserializer ::Delegate {
@@ -83,11 +87,66 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
83
87
wasm_modules_(wasm_modules),
84
88
shared_value_conveyor_(shared_value_conveyor) {}
85
89
90
+ MaybeLocal<Object> ReadDOMException (Isolate* isolate,
91
+ Local<Context> context,
92
+ v8::ValueDeserializer* deserializer) {
93
+ Local<Value> name, message;
94
+ if (!deserializer->ReadValue (context).ToLocal (&name) ||
95
+ !deserializer->ReadValue (context).ToLocal (&message)) {
96
+ return MaybeLocal<Object>();
97
+ }
98
+
99
+ bool has_code = false ;
100
+ Local<Value> code;
101
+ has_code = deserializer->ReadValue (context).ToLocal (&code);
102
+
103
+ // V8 disallows executing JS code in the deserialization process, so we
104
+ // cannot create a DOMException object directly. Instead, we create a
105
+ // placeholder object that will be converted to a DOMException object
106
+ // later on.
107
+ Local<Object> placeholder = Object::New (isolate);
108
+ if (placeholder
109
+ ->Set (context,
110
+ String::NewFromUtf8 (isolate, " __domexception_name" )
111
+ .ToLocalChecked (),
112
+ name)
113
+ .IsNothing () ||
114
+ placeholder
115
+ ->Set (context,
116
+ String::NewFromUtf8 (isolate, " __domexception_message" )
117
+ .ToLocalChecked (),
118
+ message)
119
+ .IsNothing () ||
120
+ (has_code &&
121
+ placeholder
122
+ ->Set (context,
123
+ String::NewFromUtf8 (isolate, " __domexception_code" )
124
+ .ToLocalChecked (),
125
+ code)
126
+ .IsNothing ()) ||
127
+ placeholder
128
+ ->Set (context,
129
+ String::NewFromUtf8 (isolate, " __domexception_placeholder" )
130
+ .ToLocalChecked (),
131
+ v8::True (isolate))
132
+ .IsNothing ()) {
133
+ return MaybeLocal<Object>();
134
+ }
135
+
136
+ return placeholder;
137
+ }
138
+
86
139
MaybeLocal<Object> ReadHostObject (Isolate* isolate) override {
87
140
// Identifying the index in the message's BaseObject array is sufficient.
88
141
uint32_t id;
89
142
if (!deserializer->ReadUint32 (&id))
90
143
return MaybeLocal<Object>();
144
+
145
+ Local<Context> context = isolate->GetCurrentContext ();
146
+ if (id == kDOMExceptionTag ) {
147
+ return ReadDOMException (isolate, context, deserializer);
148
+ }
149
+
91
150
if (id != kNormalObject ) {
92
151
CHECK_LT (id, host_objects_.size ());
93
152
Local<Object> object = host_objects_[id]->object (isolate);
@@ -98,7 +157,6 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
98
157
}
99
158
}
100
159
EscapableHandleScope scope (isolate);
101
- Local<Context> context = isolate->GetCurrentContext ();
102
160
Local<Value> object;
103
161
if (!deserializer->ReadValue (context).ToLocal (&object))
104
162
return MaybeLocal<Object>();
@@ -136,6 +194,71 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
136
194
137
195
} // anonymous namespace
138
196
197
+ MaybeLocal<Object> ConvertDOMExceptionData (Local<Context> context,
198
+ Local<Value> value) {
199
+ if (!value->IsObject ()) return MaybeLocal<Object>();
200
+
201
+ Isolate* isolate = context->GetIsolate ();
202
+ Local<Object> obj = value.As <Object>();
203
+
204
+ Local<String> marker_key =
205
+ String::NewFromUtf8 (isolate, " __domexception_placeholder" )
206
+ .ToLocalChecked ();
207
+ Local<Value> marker_val;
208
+ if (!obj->Get (context, marker_key).ToLocal (&marker_val) ||
209
+ !marker_val->IsTrue ()) {
210
+ return MaybeLocal<Object>();
211
+ }
212
+
213
+ Local<String> name_key =
214
+ String::NewFromUtf8 (isolate, " __domexception_name" ).ToLocalChecked ();
215
+ Local<String> message_key =
216
+ String::NewFromUtf8 (isolate, " __domexception_message" ).ToLocalChecked ();
217
+ Local<String> code_key =
218
+ String::NewFromUtf8 (isolate, " __domexception_code" ).ToLocalChecked ();
219
+
220
+ Local<Value> name, message, code;
221
+ if (!obj->Get (context, name_key).ToLocal (&name) ||
222
+ !obj->Get (context, message_key).ToLocal (&message)) {
223
+ return MaybeLocal<Object>();
224
+ }
225
+ bool has_code = obj->Get (context, code_key).ToLocal (&code);
226
+
227
+ Local<Function> dom_exception_ctor;
228
+ if (!GetDOMException (context).ToLocal (&dom_exception_ctor)) {
229
+ return MaybeLocal<Object>();
230
+ }
231
+
232
+ // Create arguments for the constructor according to the JS implementation
233
+ // First arg: message
234
+ // Second arg: options object with name and potentially code
235
+ Local<Object> options = Object::New (isolate);
236
+ if (options
237
+ ->Set (context,
238
+ String::NewFromUtf8 (isolate, " name" ).ToLocalChecked (),
239
+ name)
240
+ .IsNothing ()) {
241
+ return MaybeLocal<Object>();
242
+ }
243
+
244
+ if (has_code &&
245
+ options
246
+ ->Set (context,
247
+ String::NewFromUtf8 (isolate, " code" ).ToLocalChecked (),
248
+ code)
249
+ .IsNothing ()) {
250
+ return MaybeLocal<Object>();
251
+ }
252
+
253
+ Local<Value> argv[2 ] = {message, options};
254
+ Local<Value> exception;
255
+ if (!dom_exception_ctor->NewInstance (context, 2 , argv).ToLocal (&exception)) {
256
+ return MaybeLocal<Object>();
257
+ }
258
+
259
+ return exception.As <Object>();
260
+ }
261
+
139
262
MaybeLocal<Value> Message::Deserialize (Environment* env,
140
263
Local<Context> context,
141
264
Local<Value>* port_list) {
@@ -227,8 +350,14 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
227
350
return {};
228
351
}
229
352
230
- host_objects.clear ();
231
- return handle_scope.Escape (return_value);
353
+ Local<Object> converted_dom_exception;
354
+ if (!ConvertDOMExceptionData (context, return_value)
355
+ .ToLocal (&converted_dom_exception)) {
356
+ host_objects.clear ();
357
+ return handle_scope.Escape (return_value);
358
+ }
359
+
360
+ return handle_scope.Escape (converted_dom_exception);
232
361
}
233
362
234
363
void Message::AddSharedArrayBuffer (
@@ -294,6 +423,37 @@ void ThrowDataCloneException(Local<Context> context, Local<String> message) {
294
423
isolate->ThrowException (exception);
295
424
}
296
425
426
+ Maybe<bool > IsDOMException (Isolate* isolate,
427
+ Local<Context> context,
428
+ Local<Object> obj) {
429
+ HandleScope handle_scope (isolate);
430
+
431
+ Local<Object> per_context_bindings;
432
+ Local<Value> dom_exception_ctor_val;
433
+
434
+ if (!GetPerContextExports (context).ToLocal (&per_context_bindings)) {
435
+ return Nothing<bool >();
436
+ }
437
+
438
+ if (!per_context_bindings
439
+ ->Get (context,
440
+ String::NewFromUtf8 (isolate, " DOMException" ).ToLocalChecked ())
441
+ .ToLocal (&dom_exception_ctor_val) ||
442
+ !dom_exception_ctor_val->IsFunction ()) {
443
+ return Nothing<bool >();
444
+ }
445
+
446
+ Local<Function> dom_exception_ctor = dom_exception_ctor_val.As <Function>();
447
+
448
+ Maybe<bool > result = obj->InstanceOf (context, dom_exception_ctor);
449
+
450
+ if (result.IsNothing ()) {
451
+ return Nothing<bool >();
452
+ }
453
+
454
+ return Just (result.FromJust ());
455
+ }
456
+
297
457
// This tells V8 how to serialize objects that it does not understand
298
458
// (e.g. C++ objects) into the output buffer, in a way that our own
299
459
// DeserializerDelegate understands how to unpack.
@@ -313,6 +473,9 @@ class SerializerDelegate : public ValueSerializer::Delegate {
313
473
return Just (true );
314
474
}
315
475
476
+ Maybe<bool > is_dom_exception = IsDOMException (isolate, context_, object);
477
+ if (!is_dom_exception.IsNothing () && is_dom_exception.FromJust ()) return Just (true );
478
+
316
479
return Just (JSTransferable::IsJSTransferable (env_, context_, object));
317
480
}
318
481
@@ -328,6 +491,11 @@ class SerializerDelegate : public ValueSerializer::Delegate {
328
491
return WriteHostObject (js_transferable);
329
492
}
330
493
494
+ Maybe<bool > is_dom_exception = IsDOMException (isolate, context_, object);
495
+ if (!is_dom_exception.IsNothing () && is_dom_exception.FromJust ()) {
496
+ return WriteDOMException (context_, object);
497
+ }
498
+
331
499
// Convert process.env to a regular object.
332
500
auto env_proxy_ctor_template = env_->env_proxy_ctor_template ();
333
501
if (!env_proxy_ctor_template.IsEmpty () &&
@@ -424,6 +592,26 @@ class SerializerDelegate : public ValueSerializer::Delegate {
424
592
ValueSerializer* serializer = nullptr ;
425
593
426
594
private:
595
+ Maybe<bool > WriteDOMException (Local<Context> context,
596
+ Local<Object> exception) {
597
+ serializer->WriteUint32 (kDOMExceptionTag );
598
+
599
+ Local<Value> name_val, message_val, code_val;
600
+ if (!exception->Get (context, env_->name_string ()).ToLocal (&name_val) ||
601
+ !exception->Get (context, env_->message_string ())
602
+ .ToLocal (&message_val) ||
603
+ !exception->Get (context, env_->code_string ()).ToLocal (&code_val)) {
604
+ return Nothing<bool >();
605
+ }
606
+
607
+ if (serializer->WriteValue (context, name_val).IsNothing () ||
608
+ serializer->WriteValue (context, message_val).IsNothing () ||
609
+ serializer->WriteValue (context, code_val).IsNothing ()) {
610
+ return Nothing<bool >();
611
+ }
612
+
613
+ return Just (true );
614
+ }
427
615
Maybe<bool > WriteHostObject (BaseObjectPtr<BaseObject> host_object) {
428
616
BaseObject::TransferMode mode = host_object->GetTransferMode ();
429
617
if (mode == TransferMode::kDisallowCloneAndTransfer ) {
0 commit comments