@@ -165,6 +165,10 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
165165 input = input_buffer.ToString ();
166166 } else if (args[0 ]->IsObject ()) {
167167 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 ;
168172 } else {
169173 THROW_ERR_INVALID_ARG_TYPE (env, " Input must be an object or a string" );
170174 return ;
@@ -180,7 +184,9 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
180184 CHECK (!options.has_value ());
181185 options = URLPatternOptions::FromJsObject (env, args[1 ].As <Object>());
182186 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.
184190 return ;
185191 }
186192 } else {
@@ -197,7 +203,9 @@ void URLPattern::New(const FunctionCallbackInfo<Value>& args) {
197203 CHECK (!options.has_value ());
198204 options = URLPatternOptions::FromJsObject (env, args[2 ].As <Object>());
199205 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.
201209 return ;
202210 }
203211 }
@@ -234,73 +242,29 @@ MaybeLocal<Value> URLPattern::URLPatternInit::ToJsObject(
234242 auto isolate = env->isolate ();
235243 auto context = env->context ();
236244 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 {};
299263 }
300264 return result;
301265}
302266
303- ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject (
267+ std::optional< ada::url_pattern_init> URLPattern::URLPatternInit::FromJsObject (
304268 Environment* env, Local<Object> obj) {
305269 ada::url_pattern_init init{};
306270 Local<String> components[] = {
@@ -344,6 +308,10 @@ ada::url_pattern_init URLPattern::URLPatternInit::FromJsObject(
344308 Utf8Value utf8_value (isolate, value);
345309 set_parameter (key.ToStringView (), utf8_value.ToStringView ());
346310 }
311+ } else {
312+ // If ToLocal failed then we assume an error occurred,
313+ // bail out early to propagate the error.
314+ return std::nullopt ;
347315 }
348316 }
349317 return init;
@@ -355,12 +323,8 @@ MaybeLocal<Object> URLPattern::URLPatternComponentResult::ToJSObject(
355323 auto context = env->context ();
356324 auto parsed_group = Object::New (isolate);
357325 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)) {
364328 return {};
365329 }
366330 Local<Value> value;
@@ -371,7 +335,9 @@ MaybeLocal<Object> URLPattern::URLPatternComponentResult::ToJSObject(
371335 } else {
372336 value = Undefined (isolate);
373337 }
374- USE (parsed_group->Set (context, key, value));
338+ if (parsed_group->Set (context, key, value).IsNothing ()) {
339+ return {};
340+ }
375341 }
376342 Local<Value> input;
377343 if (!ToV8Value (env->context (), result.input ).ToLocal (&input)) {
@@ -418,34 +384,20 @@ MaybeLocal<Value> URLPattern::URLPatternResult::ToJSValue(
418384 LocalVector<Value> values (isolate, arraysize (names));
419385 values[0 ] = Array::New (isolate, inputs.data (), inputs.size ());
420386 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 )
449401 .ToLocal (&values[8 ])) {
450402 return {};
451403 }
@@ -461,9 +413,15 @@ URLPattern::URLPatternOptions::FromJsObject(Environment* env,
461413 if (obj->Get (env->context (), env->ignore_case_string ())
462414 .ToLocal (&ignore_case)) {
463415 if (!ignore_case->IsBoolean ()) {
416+ THROW_ERR_INVALID_ARG_TYPE (env, " options.ignoreCase must be a boolean" );
464417 return std::nullopt ;
465418 }
466419 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 ;
467425 }
468426 return options;
469427}
@@ -553,7 +511,9 @@ void URLPattern::Exec(const FunctionCallbackInfo<Value>& args) {
553511 input_base = input_value.ToString ();
554512 input = std::string_view (input_base);
555513 } 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);
557517 } else {
558518 THROW_ERR_INVALID_ARG_TYPE (
559519 env, " URLPattern input needs to be a string or an object" );
@@ -594,7 +554,9 @@ void URLPattern::Test(const FunctionCallbackInfo<Value>& args) {
594554 input_base = input_value.ToString ();
595555 input = std::string_view (input_base);
596556 } 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);
598560 } else {
599561 THROW_ERR_INVALID_ARG_TYPE (
600562 env, " URLPattern input needs to be a string or an object" );
0 commit comments