diff --git a/src/workerd/api/url-standard.c++ b/src/workerd/api/url-standard.c++ index 06f49272ead..ba89a1ebf1b 100644 --- a/src/workerd/api/url-standard.c++ +++ b/src/workerd/api/url-standard.c++ @@ -2079,25 +2079,24 @@ jsg::Ref URLSearchParams::values(jsg::Lock&) { void URLSearchParams::forEach( jsg::Lock& js, - jsg::V8Ref callback, + jsg::Function)> callback, jsg::Optional thisArg) { - auto cb = callback.getHandle(js); - auto this_ = thisArg.map([&js](jsg::Value& v) { return v.getHandle(js); }) - .orDefault(js.v8Undefined()); - auto query = KJ_ASSERT_NONNULL(JSG_THIS.tryGetHandle(js.v8Isolate)); - // On each iteration of the for loop, a JavaScript callback is invokved. If a new + auto receiver = js.v8Undefined(); + KJ_IF_MAYBE(arg, thisArg) { + auto handle = arg->getHandle(js); + if (!handle->IsNullOrUndefined()) { + receiver = handle; + } + } + callback.setReceiver(js.v8Ref(receiver)); + // On each iteration of the for loop, a JavaScript callback is invoked. If a new // item is appended to the URLSearchParams within that function, the loop must pick // it up. Using the classic for (;;) syntax here allows for that. However, this does // mean that it's possible for a user to trigger an infinite loop here if new items // are added to the search params unconditionally on each iteration. for (size_t i = 0; i < list.size(); i++) { auto& entry = list[i]; - v8::Local args[3] = { - jsg::v8Str(js.v8Isolate, entry.value), - jsg::v8Str(js.v8Isolate, entry.name), - query, - }; - jsg::check(cb->Call(js.v8Context(), this_, 3, args)); + callback(js, entry.value, entry.name, JSG_THIS); } } diff --git a/src/workerd/api/url-standard.h b/src/workerd/api/url-standard.h index cb6450de60d..7346bde67ce 100644 --- a/src/workerd/api/url-standard.h +++ b/src/workerd/api/url-standard.h @@ -116,10 +116,9 @@ class URLSearchParams: public jsg::Object { IteratorState, valueIteratorNext) - void forEach( - jsg::Lock& js, - jsg::V8Ref callback, - jsg::Optional thisArg); + void forEach(jsg::Lock&, + jsg::Function)>, + jsg::Optional); jsg::UsvString toString(); diff --git a/src/workerd/api/url.c++ b/src/workerd/api/url.c++ index 090eb73a272..8e49c44851b 100644 --- a/src/workerd/api/url.c++ +++ b/src/workerd/api/url.c++ @@ -587,31 +587,25 @@ void URLSearchParams::sort() { void URLSearchParams::forEach( jsg::Lock& js, - jsg::V8Ref callback, + jsg::Function)> callback, jsg::Optional thisArg) { - auto localCallback = callback.getHandle(js); - auto localThisArg = thisArg.map([&js](jsg::Value& v) { return v.getHandle(js); }) - .orDefault(js.v8Undefined()); - // JSG_THIS.getHandle() is guaranteed safe because `forEach()` is only called - // from JavaScript, which means a Headers JS wrapper object must already exist. - auto localParams = KJ_ASSERT_NONNULL(JSG_THIS.tryGetHandle(js.v8Isolate)); - - // On each iteration of the for loop, a JavaScript callback is invokved. If a new + auto receiver = js.v8Undefined(); + KJ_IF_MAYBE(arg, thisArg) { + auto handle = arg->getHandle(js); + if (!handle->IsNullOrUndefined()) { + receiver = handle; + } + } + callback.setReceiver(js.v8Ref(receiver)); + + // On each iteration of the for loop, a JavaScript callback is invoked. If a new // item is appended to the this->url->query within that function, the loop must pick // it up. Using the classic for (;;) syntax here allows for that. However, this does // mean that it's possible for a user to trigger an infinite loop here if new items // are added to the search params unconditionally on each iteration. for (size_t i = 0; i < this->url->query.size(); i++) { auto& [key, value] = this->url->query[i]; - static constexpr auto ARG_COUNT = 3; - v8::Local args[ARG_COUNT] = { - jsg::v8Str(js.v8Isolate, value), - jsg::v8Str(js.v8Isolate, key), - localParams, - }; - // Call jsg::check() to propagate exceptions, but we don't expect any - // particular return value. - jsg::check(localCallback->Call(js.v8Context(), localThisArg, ARG_COUNT, args)); + callback(js, value, key, JSG_THIS); } } diff --git a/src/workerd/api/url.h b/src/workerd/api/url.h index f89aae4871c..b038992c973 100644 --- a/src/workerd/api/url.h +++ b/src/workerd/api/url.h @@ -161,10 +161,9 @@ class URLSearchParams: public jsg::Object { IteratorState, valueIteratorNext) - void forEach( - jsg::Lock& js, - jsg::V8Ref callback, - jsg::Optional thisArg); + void forEach(jsg::Lock&, + jsg::Function)>, + jsg::Optional); kj::String toString();