Repository: electron/electron
Pull request: perf: hold `Emit()` arg arrays in a `std::array`

#### Description of Change

A followup to #43729. We know the size of the args list at compile time, so we can use a statically-allocated array instead of a heap-allocated vector, e.g.

```diff
- std::vector<Local> items = { (args)... }
+ std::array<Local, sizeof...(args)> items = { (args)... }
```

#### Checklist

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: none.

Changed files:
filename: shell/common/gin_helper/event_emitter_caller.cc
patch: @@ -9,10 +9,11 @@
 
 namespace gin_helper::internal {
 
-v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
-                                        v8::Local<v8::Object> obj,
-                                        const char* method,
-                                        ValueVector* args) {
+v8::Local<v8::Value> CallMethodWithArgs(
+    v8::Isolate* isolate,
+    v8::Local<v8::Object> obj,
+    const char* method,
+    const base::span<v8::Local<v8::Value>> args) {
   v8::EscapableHandleScope handle_scope{isolate};
 
   // CallbackScope and MakeCallback both require an active node::Environment
@@ -29,7 +30,7 @@ v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
 
   // node::MakeCallback will also run pending tasks in Node.js.
   v8::MaybeLocal<v8::Value> ret = node::MakeCallback(
-      isolate, obj, method, args->size(), args->data(), {0, 0});
+      isolate, obj, method, args.size(), args.data(), {0, 0});
 
   // If the JS function throws an exception (doesn't return a value) the result
   // of MakeCallback will be empty and therefore ToLocal will be false, in this

filename: shell/common/gin_helper/event_emitter_caller.h
patch: @@ -5,38 +5,24 @@
 #ifndef ELECTRON_SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_CALLER_H_
 #define ELECTRON_SHELL_COMMON_GIN_HELPER_EVENT_EMITTER_CALLER_H_
 
+#include <array>
 #include <utility>
-#include <vector>
 
+#include "base/containers/span.h"
 #include "gin/converter.h"
 #include "gin/wrappable.h"
 
 namespace gin_helper {
 
 namespace internal {
 
-using ValueVector = std::vector<v8::Local<v8::Value>>;
-
 v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
                                         v8::Local<v8::Object> obj,
                                         const char* method,
-                                        ValueVector* args);
+                                        base::span<v8::Local<v8::Value>> args);
 
 }  // namespace internal
 
-// obj.emit.apply(obj, name, args...);
-// The caller is responsible of allocating a HandleScope.
-template <typename StringType>
-v8::Local<v8::Value> EmitEvent(v8::Isolate* isolate,
-                               v8::Local<v8::Object> obj,
-                               const StringType& name,
-                               const internal::ValueVector& args) {
-  internal::ValueVector concatenated_args = {gin::StringToV8(isolate, name)};
-  concatenated_args.reserve(1 + args.size());
-  concatenated_args.insert(concatenated_args.end(), args.begin(), args.end());
-  return internal::CallMethodWithArgs(isolate, obj, "emit", &concatenated_args);
-}
-
 // obj.emit(name, args...);
 // The caller is responsible of allocating a HandleScope.
 template <typename StringType, typename... Args>
@@ -45,12 +31,12 @@ v8::Local<v8::Value> EmitEvent(v8::Isolate* isolate,
                                const StringType& name,
                                Args&&... args) {
   v8::EscapableHandleScope scope{isolate};
-  internal::ValueVector converted_args = {
+  std::array<v8::Local<v8::Value>, 1U + sizeof...(args)> converted_args = {
       gin::StringToV8(isolate, name),
       gin::ConvertToV8(isolate, std::forward<Args>(args))...,
   };
   return scope.Escape(
-      internal::CallMethodWithArgs(isolate, obj, "emit", &converted_args));
+      internal::CallMethodWithArgs(isolate, obj, "emit", converted_args));
 }
 
 // obj.custom_emit(args...)
@@ -60,11 +46,11 @@ v8::Local<v8::Value> CustomEmit(v8::Isolate* isolate,
                                 const char* custom_emit,
                                 Args&&... args) {
   v8::EscapableHandleScope scope{isolate};
-  internal::ValueVector converted_args = {
+  std::array<v8::Local<v8::Value>, sizeof...(args)> converted_args = {
       gin::ConvertToV8(isolate, std::forward<Args>(args))...,
   };
   return scope.Escape(internal::CallMethodWithArgs(isolate, object, custom_emit,
-                                                   &converted_args));
+                                                   converted_args));
 }
 
 template <typename T, typename... Args>