@@ -66,6 +66,10 @@ bool Message::IsCloseMessage() const {
6666
6767namespace  {
6868
69+ MaybeLocal<Function> GetDOMException (Local<Context> context);
70+ 
71+ static  const  uint32_t  kDOMExceptionTag  = 0xD011 ;
72+ 
6973//  This is used to tell V8 how to read transferred host objects, like other
7074//  `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them.
7175class  DeserializerDelegate  : public  ValueDeserializer ::Delegate {
@@ -83,11 +87,66 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
8387        wasm_modules_(wasm_modules),
8488        shared_value_conveyor_(shared_value_conveyor) {}
8589
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+ 
86139  MaybeLocal<Object> ReadHostObject (Isolate* isolate) override  {
87140    //  Identifying the index in the message's BaseObject array is sufficient.
88141    uint32_t  id;
89142    if  (!deserializer->ReadUint32 (&id))
90143      return  MaybeLocal<Object>();
144+ 
145+     Local<Context> context = isolate->GetCurrentContext ();
146+     if  (id == kDOMExceptionTag ) {
147+       return  ReadDOMException (isolate, context, deserializer);
148+     }
149+ 
91150    if  (id != kNormalObject ) {
92151      CHECK_LT (id, host_objects_.size ());
93152      Local<Object> object = host_objects_[id]->object (isolate);
@@ -98,7 +157,6 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
98157      }
99158    }
100159    EscapableHandleScope scope (isolate);
101-     Local<Context> context = isolate->GetCurrentContext ();
102160    Local<Value> object;
103161    if  (!deserializer->ReadValue (context).ToLocal (&object))
104162      return  MaybeLocal<Object>();
@@ -136,6 +194,71 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
136194
137195}  //  anonymous namespace
138196
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+ 
139262MaybeLocal<Value> Message::Deserialize (Environment* env,
140263                                       Local<Context> context,
141264                                       Local<Value>* port_list) {
@@ -227,8 +350,14 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
227350      return  {};
228351  }
229352
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);
232361}
233362
234363void  Message::AddSharedArrayBuffer (
@@ -294,6 +423,37 @@ void ThrowDataCloneException(Local<Context> context, Local<String> message) {
294423  isolate->ThrowException (exception);
295424}
296425
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+ 
297457//  This tells V8 how to serialize objects that it does not understand
298458//  (e.g. C++ objects) into the output buffer, in a way that our own
299459//  DeserializerDelegate understands how to unpack.
@@ -313,6 +473,10 @@ class SerializerDelegate : public ValueSerializer::Delegate {
313473      return  Just (true );
314474    }
315475
476+     if  (IsDOMException (isolate, context_, object).FromJust ()) {
477+       return  Just (true );
478+     }
479+ 
316480    return  Just (JSTransferable::IsJSTransferable (env_, context_, object));
317481  }
318482
@@ -328,6 +492,10 @@ class SerializerDelegate : public ValueSerializer::Delegate {
328492      return  WriteHostObject (js_transferable);
329493    }
330494
495+     if  (IsDOMException (isolate, context_, object).FromJust ()) {
496+       return  WriteDOMException (context_, object);
497+     }
498+ 
331499    //  Convert process.env to a regular object.
332500    auto  env_proxy_ctor_template = env_->env_proxy_ctor_template ();
333501    if  (!env_proxy_ctor_template.IsEmpty () &&
@@ -424,6 +592,26 @@ class SerializerDelegate : public ValueSerializer::Delegate {
424592  ValueSerializer* serializer = nullptr ;
425593
426594 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+   }
427615  Maybe<bool > WriteHostObject (BaseObjectPtr<BaseObject> host_object) {
428616    BaseObject::TransferMode mode = host_object->GetTransferMode ();
429617    if  (mode == TransferMode::kDisallowCloneAndTransfer ) {
0 commit comments