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