a058070
commit ce71348a09f6689dd01a68db64b172191d0182d8
a058070
Author: Andrey Kosyakov <caseq@chromium.org>
a058070
Date:   Thu Dec 21 18:38:38 2023 +0000
a058070
a058070
    [bindings] Use v8::Array::Iterate for converting script wrappables
a058070
    
a058070
    
a058070
    This changes CreateIDLSequenceFromV8Array to use the new
a058070
    v8::Array::Iterate() operation.
a058070
    This speeds up the "execBundles" part of the microbenchmark
a058070
    at crbug.com/dawn/1858 by around 3x.
a058070
    This depends on crrev.com/c/4846594 landing (and rolling) first.
a058070
    
a058070
    This is a slight re-work of https://crrev.com/c/4847447/3,
a058070
    originally by jkummerow@chromium.org
a058070
    
a058070
    Bug: v8:14218, dawn:1858, 1511239
a058070
    Change-Id: Ia266556d05b4d53e6942e12609d1c08882b4ff0f
a058070
    Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5132129
a058070
    Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
a058070
    Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
a058070
    Cr-Commit-Position: refs/heads/main@{#1240236}
a058070
a058070
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits.h
a058070
index 1e5a0790df6da..a5c28b37e9454 100644
a058070
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits.h
a058070
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits.h
a058070
@@ -84,6 +84,12 @@ struct NativeValueTraitsBase {
a058070
       std::is_pointer_v<ImplType> ||
a058070
       requires(ImplType value) { value.IsNull(); };
a058070
 
a058070
+  // This should only be true for certain subclasses of ScriptWrappable
a058070
+  // that satisfy the assumptions of CreateIDLSequenceFromV8ArraySlow() with
a058070
+  // regards to how NativeValue() is implemented for the underlying type.
a058070
+  static constexpr bool supports_scriptwrappable_specific_fast_array_iteration =
a058070
+      false;
a058070
+
a058070
   template <typename... ExtraArgs>
a058070
   static decltype(auto) ArgumentValue(v8::Isolate* isolate,
a058070
                                       int argument_index,
a058070
diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
a058070
index 5011503dcf1c0..f085b6e905161 100644
a058070
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
a058070
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
a058070
@@ -1037,10 +1037,86 @@ CreateIDLSequenceFromV8ArraySlow(v8::Isolate* isolate,
a058070
     return {};
a058070
   }
a058070
 
a058070
-  typename NativeValueTraits<IDLSequence<T>>::ImplType result;
a058070
+  using ResultType = typename NativeValueTraits<IDLSequence<T>>::ImplType;
a058070
+  ResultType result;
a058070
   result.ReserveInitialCapacity(length);
a058070
   v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
a058070
   v8::TryCatch try_block(isolate);
a058070
+
a058070
+  // Fast path -- we're creating a sequence of script wrappables, which can be
a058070
+  // done by directly getting underlying object as long as array types are
a058070
+  // homogeneous. With ScriptWrappables, we don't expect to enter JS during
a058070
+  // iteration, so we can rely on v8::Array::Iterate() which is much faster than
a058070
+  // iterating an array on the client side of the v8. Additionally, for most
a058070
+  // subsptyes of ScriptWrappables, we can speed up type checks (see more on
a058070
+  // that below next to supports_scriptwrappable_specific_fast_array_iteration
a058070
+  // check.
a058070
+  if constexpr (std::is_base_of_v<ScriptWrappable, T>) {
a058070
+    struct CallbackData {
a058070
+      STACK_ALLOCATED();
a058070
+
a058070
+     public:
a058070
+      v8::Isolate* isolate;
a058070
+      v8::TypecheckWitness witness;
a058070
+      ResultType& result;
a058070
+      ExceptionState& exception_state;
a058070
+      CallbackData(v8::Isolate* isolate,
a058070
+                   ResultType& result,
a058070
+                   ExceptionState& exception_state)
a058070
+          : isolate(isolate),
a058070
+            witness(isolate),
a058070
+            result(result),
a058070
+            exception_state(exception_state) {}
a058070
+    };
a058070
+
a058070
+    CallbackData callback_data(isolate, result, exception_state);
a058070
+    v8::Array::IterationCallback callback = [](uint32_t index,
a058070
+                                               v8::Local<v8::Value> v8_element,
a058070
+                                               void* data) {
a058070
+      CallbackData* callback_data = reinterpret_cast<CallbackData*>(data);
a058070
+      // 3.4. Initialize Si to the result of converting nextItem to an IDL value
a058070
+      //   of type T.
a058070
+      v8::TypecheckWitness& witness = callback_data->witness;
a058070
+      // We can speed up type check by taking advantage of V8's type witness,
a058070
+      // provided traits' NativeValue implementation doesn't have additional
a058070
+      // logic beyond checking the type and calling ToScriptWrappable().
a058070
+      if constexpr (
a058070
+          NativeValueTraits<
a058070
+              T>::supports_scriptwrappable_specific_fast_array_iteration) {
a058070
+        if (witness.Matches(v8_element)) {
a058070
+          auto&& value = ToScriptWrappable(v8_element.As<v8::Object>())
a058070
+                             ->template ToImpl<T>();
a058070
+          callback_data->result.push_back(std::move(value));
a058070
+          return v8::Array::CallbackResult::kContinue;
a058070
+        }
a058070
+      }
a058070
+      auto&& element = NativeValueTraits<T>::NativeValue(
a058070
+          callback_data->isolate, v8_element, callback_data->exception_state);
a058070
+      if (callback_data->exception_state.HadException()) {
a058070
+        // It doesn't matter whether we return `kException` or `kBreak` here,
a058070
+        // as that only affects the return value of `v8_array->Iterate()`,
a058070
+        // which we are ignoring.
a058070
+        return v8::Array::CallbackResult::kException;
a058070
+      }
a058070
+      if constexpr (
a058070
+          NativeValueTraits<
a058070
+              T>::supports_scriptwrappable_specific_fast_array_iteration) {
a058070
+        witness.Update(v8_element);
a058070
+      }
a058070
+      callback_data->result.push_back(std::move(element));
a058070
+      return v8::Array::CallbackResult::kContinue;
a058070
+    };
a058070
+    if (!v8_array->Iterate(current_context, callback, &callback_data)
a058070
+             .IsJust()) {
a058070
+      if (try_block.HasCaught()) {
a058070
+        exception_state.RethrowV8Exception(try_block.Exception());
a058070
+      }
a058070
+      DCHECK(exception_state.HadException());
a058070
+      return {};
a058070
+    }
a058070
+    return result;
a058070
+  }
a058070
+
a058070
   // Array length may change if array is mutated during iteration.
a058070
   for (uint32_t i = 0; i < v8_array->Length(); ++i) {
a058070
     v8::Local<v8::Value> v8_element;
a058070
@@ -1056,6 +1132,7 @@ CreateIDLSequenceFromV8ArraySlow(v8::Isolate* isolate,
a058070
       return {};
a058070
     result.push_back(std::move(element));
a058070
   }
a058070
+
a058070
   // 3.2. If next is false, then return an IDL sequence value of type
a058070
   //   sequence<T> of length i, where the value of the element at index j is Sj.
a058070
   return result;
a058070
@@ -1398,6 +1475,7 @@ struct NativeValueTraits<T> : public NativeValueTraitsBase<T*> {
a058070
   }
a058070
 };
a058070
 
a058070
+// Interface types
a058070
 template <typename T>
a058070
   requires std::derived_from<T, CallbackInterfaceBase>
a058070
 struct NativeValueTraits<IDLNullable<T>>
a058070
@@ -1470,12 +1548,21 @@ struct NativeValueTraits<T> : public NativeValueTraitsBase<T> {
a058070
 template <typename T>
a058070
   requires std::derived_from<T, ScriptWrappable>
a058070
 struct NativeValueTraits<T> : public NativeValueTraitsBase<T*> {
a058070
+  // This signifies that CreateIDLSequenceFromV8ArraySlow() may apply
a058070
+  // certain optimization based on assumptions about `NativeValue()`
a058070
+  // implementation below. For subclasses of ScriptWrappable that have
a058070
+  // different implementation of NativeValue(), this should remain false.
a058070
+  static constexpr bool supports_scriptwrappable_specific_fast_array_iteration =
a058070
+      true;
a058070
+
a058070
   static inline T* NativeValue(v8::Isolate* isolate,
a058070
                                v8::Local<v8::Value> value,
a058070
                                ExceptionState& exception_state) {
a058070
     const WrapperTypeInfo* wrapper_type_info = T::GetStaticWrapperTypeInfo();
a058070
-    if (V8PerIsolateData::From(isolate)->HasInstance(wrapper_type_info, value))
a058070
+    if (V8PerIsolateData::From(isolate)->HasInstance(wrapper_type_info,
a058070
+                                                     value)) {
a058070
       return ToScriptWrappable(value.As<v8::Object>())->template ToImpl<T>();
a058070
+    }
a058070
 
a058070
     bindings::NativeValueTraitsInterfaceNotOfType(wrapper_type_info,
a058070
                                                   exception_state);