@@ -165,6 +165,10 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
165
165
input = input_buffer.ToString ();
166
166
} else if (args[0 ]->IsObject ()) {
167
167
init = URLPatternInit::FromJsObject (env, args[0 ].As <Object>());
168
+ // If init does not have a value here, the implication is that an
169
+ // error was thrown. Let's allow that to be handled now by returning
170
+ // early. If we don't, the error thrown will be swallowed.
171
+ if (!init) return ;
168
172
} else {
169
173
THROW_ERR_INVALID_ARG_TYPE (env, " Input must be an object or a string" );
170
174
return ;
@@ -180,7 +184,9 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
180
184
CHECK (!options.has_value ());
181
185
options = URLPatternOptions::FromJsObject (env, args[1 ].As <Object>());
182
186
if (!options) {
183
- THROW_ERR_INVALID_ARG_TYPE (env, " options.ignoreCase must be a boolean" );
187
+ // If options does not have a value, we assume an error was
188
+ // thrown and scheduled on the isolate. Return early to
189
+ // propagate it.
184
190
return ;
185
191
}
186
192
} else {
@@ -197,7 +203,9 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
197
203
CHECK (!options.has_value ());
198
204
options = URLPatternOptions::FromJsObject (env, args[2 ].As <Object>());
199
205
if (!options) {
200
- THROW_ERR_INVALID_ARG_TYPE (env, " options.ignoreCase must be a boolean" );
206
+ // If options does not have a value, we assume an error was
207
+ // thrown and scheduled on the isolate. Return early to
208
+ // propagate it.
201
209
return ;
202
210
}
203
211
}
@@ -234,73 +242,29 @@ MaybeLocal<Value> URLPattern::URLPatternInit::ToJsObject(
234
242
auto isolate = env->isolate ();
235
243
auto context = env->context ();
236
244
auto result = Object::New (isolate);
237
- if (init.protocol ) {
238
- Local<Value> protocol;
239
- if (!ToV8Value (context, *init.protocol ).ToLocal (&protocol) ||
240
- result->Set (context, env->protocol_string (), protocol).IsNothing ()) {
241
- return {};
242
- }
243
- }
244
- if (init.username ) {
245
- Local<Value> username;
246
- if (!ToV8Value (context, *init.username ).ToLocal (&username) ||
247
- result->Set (context, env->username_string (), username).IsNothing ()) {
248
- return {};
249
- }
250
- }
251
- if (init.password ) {
252
- Local<Value> password;
253
- if (!ToV8Value (context, *init.password ).ToLocal (&password) ||
254
- result->Set (context, env->password_string (), password).IsNothing ()) {
255
- return {};
256
- }
257
- }
258
- if (init.hostname ) {
259
- Local<Value> hostname;
260
- if (!ToV8Value (context, *init.hostname ).ToLocal (&hostname) ||
261
- result->Set (context, env->hostname_string (), hostname).IsNothing ()) {
262
- return {};
263
- }
264
- }
265
- if (init.port ) {
266
- Local<Value> port;
267
- if (!ToV8Value (context, *init.port ).ToLocal (&port) ||
268
- result->Set (context, env->port_string (), port).IsNothing ()) {
269
- return {};
270
- }
271
- }
272
- if (init.pathname ) {
273
- Local<Value> pathname;
274
- if (!ToV8Value (context, *init.pathname ).ToLocal (&pathname) ||
275
- result->Set (context, env->pathname_string (), pathname).IsNothing ()) {
276
- return {};
277
- }
278
- }
279
- if (init.search ) {
280
- Local<Value> search;
281
- if (!ToV8Value (context, *init.search ).ToLocal (&search) ||
282
- result->Set (context, env->search_string (), search).IsNothing ()) {
283
- return {};
284
- }
285
- }
286
- if (init.hash ) {
287
- Local<Value> hash;
288
- if (!ToV8Value (context, *init.hash ).ToLocal (&hash) ||
289
- result->Set (context, env->hash_string (), hash).IsNothing ()) {
290
- return {};
291
- }
292
- }
293
- if (init.base_url ) {
294
- Local<Value> base_url;
295
- if (!ToV8Value (context, *init.base_url ).ToLocal (&base_url) ||
296
- result->Set (context, env->base_url_string (), base_url).IsNothing ()) {
297
- return {};
298
- }
245
+
246
+ const auto trySet = [&](auto name, const std::optional<std::string>& val) {
247
+ if (!val) return true ;
248
+ Local<Value> temp;
249
+ return ToV8Value (context, *val).ToLocal (&temp) &&
250
+ result->Set (context, name, temp).IsJust ();
251
+ };
252
+
253
+ if (!trySet (env->protocol_string (), init.protocol ) ||
254
+ !trySet (env->username_string (), init.username ) ||
255
+ !trySet (env->password_string (), init.password ) ||
256
+ !trySet (env->hostname_string (), init.hostname ) ||
257
+ !trySet (env->port_string (), init.port ) ||
258
+ !trySet (env->pathname_string (), init.pathname ) ||
259
+ !trySet (env->search_string (), init.search ) ||
260
+ !trySet (env->hash_string (), init.hash ) ||
261
+ !trySet (env->base_url_string (), init.base_url )) {
262
+ return {};
299
263
}
300
264
return result;
301
265
}
302
266
303
- ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject (
267
+ std::optional< ada::url_pattern_init> URLPattern::URLPatternInit::FromJsObject (
304
268
Environment* env, Local<Object> obj) {
305
269
ada::url_pattern_init init{};
306
270
Local<String> components[] = {
@@ -344,6 +308,10 @@ ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject(
344
308
Utf8Value utf8_value (isolate, value);
345
309
set_parameter (key.ToStringView (), utf8_value.ToStringView ());
346
310
}
311
+ } else {
312
+ // If ToLocal failed then we assume an error occurred,
313
+ // bail out early to propagate the error.
314
+ return std::nullopt;
347
315
}
348
316
}
349
317
return init;
@@ -355,12 +323,8 @@ MaybeLocal<Object> URLPattern::URLPatternComponentResult::ToJSObject(
355
323
auto context = env->context ();
356
324
auto parsed_group = Object::New (isolate);
357
325
for (const auto & [group_key, group_value] : result.groups ) {
358
- Local<String> key;
359
- if (!String::NewFromUtf8 (isolate,
360
- group_key.c_str (),
361
- NewStringType::kNormal ,
362
- group_key.size ())
363
- .ToLocal (&key)) {
326
+ Local<Value> key;
327
+ if (!ToV8Value (context, group_key).ToLocal (&key)) {
364
328
return {};
365
329
}
366
330
Local<Value> value;
@@ -371,7 +335,9 @@ MaybeLocal<Object> URLPattern::URLPatternComponentResult::ToJSObject(
371
335
} else {
372
336
value = Undefined (isolate);
373
337
}
374
- USE (parsed_group->Set (context, key, value));
338
+ if (parsed_group->Set (context, key, value).IsNothing ()) {
339
+ return {};
340
+ }
375
341
}
376
342
Local<Value> input;
377
343
if (!ToV8Value (env->context (), result.input ).ToLocal (&input)) {
@@ -418,34 +384,20 @@ MaybeLocal<Value> URLPattern::URLPatternResult::ToJSValue(
418
384
LocalVector<Value> values (isolate, arraysize (names));
419
385
values[0 ] = Array::New (isolate, inputs.data (), inputs.size ());
420
386
if (!URLPatternComponentResult::ToJSObject (env, result.protocol )
421
- .ToLocal (&values[1 ])) {
422
- return {};
423
- }
424
- if (!URLPatternComponentResult::ToJSObject (env, result.username )
425
- .ToLocal (&values[2 ])) {
426
- return {};
427
- }
428
- if (!URLPatternComponentResult::ToJSObject (env, result.password )
429
- .ToLocal (&values[3 ])) {
430
- return {};
431
- }
432
- if (!URLPatternComponentResult::ToJSObject (env, result.hostname )
433
- .ToLocal (&values[4 ])) {
434
- return {};
435
- }
436
- if (!URLPatternComponentResult::ToJSObject (env, result.port )
437
- .ToLocal (&values[5 ])) {
438
- return {};
439
- }
440
- if (!URLPatternComponentResult::ToJSObject (env, result.pathname )
441
- .ToLocal (&values[6 ])) {
442
- return {};
443
- }
444
- if (!URLPatternComponentResult::ToJSObject (env, result.search )
445
- .ToLocal (&values[7 ])) {
446
- return {};
447
- }
448
- if (!URLPatternComponentResult::ToJSObject (env, result.hash )
387
+ .ToLocal (&values[1 ]) ||
388
+ !URLPatternComponentResult::ToJSObject (env, result.username )
389
+ .ToLocal (&values[2 ]) ||
390
+ !URLPatternComponentResult::ToJSObject (env, result.password )
391
+ .ToLocal (&values[3 ]) ||
392
+ !URLPatternComponentResult::ToJSObject (env, result.hostname )
393
+ .ToLocal (&values[4 ]) ||
394
+ !URLPatternComponentResult::ToJSObject (env, result.port )
395
+ .ToLocal (&values[5 ]) ||
396
+ !URLPatternComponentResult::ToJSObject (env, result.pathname )
397
+ .ToLocal (&values[6 ]) ||
398
+ !URLPatternComponentResult::ToJSObject (env, result.search )
399
+ .ToLocal (&values[7 ]) ||
400
+ !URLPatternComponentResult::ToJSObject (env, result.hash )
449
401
.ToLocal (&values[8 ])) {
450
402
return {};
451
403
}
@@ -461,9 +413,15 @@ URLPattern::URLPatternOptions::FromJsObject(Environment* env,
461
413
if (obj->Get (env->context (), env->ignore_case_string ())
462
414
.ToLocal (&ignore_case)) {
463
415
if (!ignore_case->IsBoolean ()) {
416
+ THROW_ERR_INVALID_ARG_TYPE (env, " options.ignoreCase must be a boolean" );
464
417
return std::nullopt;
465
418
}
466
419
options.ignore_case = ignore_case->IsTrue ();
420
+ } else {
421
+ // If ToLocal returns false, the assumption is that getting the
422
+ // ignore_case_string threw an error, let's propagate that now
423
+ // by returning std::nullopt.
424
+ return std::nullopt;
467
425
}
468
426
return options;
469
427
}
@@ -553,7 +511,9 @@ void URLPattern::Exec(const FunctionCallbackInfo<Value>& args) {
553
511
input_base = input_value.ToString ();
554
512
input = std::string_view (input_base);
555
513
} else if (args[0 ]->IsObject ()) {
556
- input = URLPatternInit::FromJsObject (env, args[0 ].As <Object>());
514
+ auto maybeInput = URLPatternInit::FromJsObject (env, args[0 ].As <Object>());
515
+ if (!maybeInput.has_value ()) return ;
516
+ input = std::move (*maybeInput);
557
517
} else {
558
518
THROW_ERR_INVALID_ARG_TYPE (
559
519
env, " URLPattern input needs to be a string or an object" );
@@ -594,7 +554,9 @@ void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {
594
554
input_base = input_value.ToString ();
595
555
input = std::string_view (input_base);
596
556
} else if (args[0 ]->IsObject ()) {
597
- input = URLPatternInit::FromJsObject (env, args[0 ].As <Object>());
557
+ auto maybeInput = URLPatternInit::FromJsObject (env, args[0 ].As <Object>());
558
+ if (!maybeInput.has_value ()) return ;
559
+ input = std::move (*maybeInput);
598
560
} else {
599
561
THROW_ERR_INVALID_ARG_TYPE (
600
562
env, " URLPattern input needs to be a string or an object" );
0 commit comments