v8  8.4.371 (node 14.19.3)
V8 is Google's open source JavaScript engine
v8-fast-api-calls.h
Go to the documentation of this file.
1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 /**
6  * This file provides additional API on top of the default one for making
7  * API calls, which come from embedder C++ functions. The functions are being
8  * called directly from optimized code, doing all the necessary typechecks
9  * in the compiler itself, instead of on the embedder side. Hence the "fast"
10  * in the name. Example usage might look like:
11  *
12  * \code
13  * void FastMethod(int param, bool another_param);
14  *
15  * v8::FunctionTemplate::New(isolate, SlowCallback, data,
16  * signature, length, constructor_behavior
17  * side_effect_type,
18  * &v8::CFunction::Make(FastMethod));
19  * \endcode
20  *
21  * An example for custom embedder type support might employ a way to wrap/
22  * unwrap various C++ types in JSObject instances, e.g:
23  *
24  * \code
25  *
26  * // Represents the way this type system maps C++ and JS values.
27  * struct WrapperTypeInfo {
28  * // Store e.g. a method to map from exposed C++ types to the already
29  * // created v8::FunctionTemplate's for instantiating them.
30  * };
31  *
32  * // Helper method with a sanity check.
33  * template <typename T, int offset>
34  * inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
35  * assert(offset < wrapper->InternalFieldCount());
36  * return reinterpret_cast<T*>(
37  * wrapper->GetAlignedPointerFromInternalField(offset));
38  * }
39  *
40  * // Returns the type info from a wrapper JS object.
41  * inline const WrapperTypeInfo* ToWrapperTypeInfo(
42  * v8::Local<v8::Object> wrapper) {
43  * return GetInternalField<WrapperTypeInfo,
44  * kV8EmbedderWrapperTypeIndex>(wrapper);
45  * }
46  *
47  * class CustomEmbedderType {
48  * public:
49  * static constexpr const WrapperTypeInfo* GetWrapperTypeInfo() {
50  * return &custom_type_wrapper_type_info;
51  * }
52  * // Returns the raw C object from a wrapper JS object.
53  * static CustomEmbedderType* Unwrap(v8::Local<v8::Object> wrapper) {
54  * return GetInternalField<CustomEmbedderType,
55  * kV8EmbedderWrapperObjectIndex>(wrapper);
56  * }
57  * static void FastMethod(CustomEmbedderType* receiver, int param) {
58  * assert(receiver != nullptr);
59  * // Type checks are already done by the optimized code.
60  * // Then call some performance-critical method like:
61  * // receiver->Method(param);
62  * }
63  *
64  * static void SlowMethod(
65  * const v8::FunctionCallbackInfo<v8::Value>& info) {
66  * v8::Local<v8::Object> instance =
67  * v8::Local<v8::Object>::Cast(info.Holder());
68  * CustomEmbedderType* receiver = Unwrap(instance);
69  * // TODO: Do type checks and extract {param}.
70  * FastMethod(receiver, param);
71  * }
72  *
73  * private:
74  * static const WrapperTypeInfo custom_type_wrapper_type_info;
75  * };
76  *
77  * // Support for custom embedder types via specialization of WrapperTraits.
78  * namespace v8 {
79  * template <>
80  * class WrapperTraits<CustomEmbedderType> {
81  * public:
82  * static const void* GetTypeInfo() {
83  * // We use the already defined machinery for the custom type.
84  * return CustomEmbedderType::GetWrapperTypeInfo();
85  * }
86  * };
87  * } // namespace v8
88  *
89  * // The constants kV8EmbedderWrapperTypeIndex and
90  * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info
91  * // struct (the one returned by WrapperTraits::GetTypeInfo) and the
92  * // native object, when expressed as internal field indices within a
93  * // JSObject. The existance of this helper function assumes that all
94  * // embedder objects have their JSObject-side type info at the same
95  * // offset, but this is not a limitation of the API itself. For a detailed
96  * // use case, see the third example.
97  * static constexpr int kV8EmbedderWrapperTypeIndex = 0;
98  * static constexpr int kV8EmbedderWrapperObjectIndex = 1;
99  *
100  * // The following setup function can be templatized based on
101  * // the {embedder_object} argument.
102  * void SetupCustomEmbedderObject(v8::Isolate* isolate,
103  * v8::Local<v8::Context> context,
104  * CustomEmbedderType* embedder_object) {
105  * isolate->set_embedder_wrapper_type_index(
106  * kV8EmbedderWrapperTypeIndex);
107  * isolate->set_embedder_wrapper_object_index(
108  * kV8EmbedderWrapperObjectIndex);
109  *
110  * v8::CFunction c_func =
111  * MakeV8CFunction(CustomEmbedderType::FastMethod);
112  *
113  * Local<v8::FunctionTemplate> method_template =
114  * v8::FunctionTemplate::New(
115  * isolate, CustomEmbedderType::SlowMethod, v8::Local<v8::Value>(),
116  * v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
117  * v8::SideEffectType::kHasSideEffect, &c_func);
118  *
119  * v8::Local<v8::ObjectTemplate> object_template =
120  * v8::ObjectTemplate::New(isolate);
121  * object_template->SetInternalFieldCount(
122  * kV8EmbedderWrapperObjectIndex + 1);
123  * object_template->Set(
124  v8::String::NewFromUtf8Literal(isolate, "method"), method_template);
125  *
126  * // Instantiate the wrapper JS object.
127  * v8::Local<v8::Object> object =
128  * object_template->NewInstance(context).ToLocalChecked();
129  * object->SetAlignedPointerInInternalField(
130  * kV8EmbedderWrapperObjectIndex,
131  * reinterpret_cast<void*>(embedder_object));
132  *
133  * // TODO: Expose {object} where it's necessary.
134  * }
135  * \endcode
136  *
137  * For instance if {object} is exposed via a global "obj" variable,
138  * one could write in JS:
139  * function hot_func() {
140  * obj.method(42);
141  * }
142  * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod
143  * will be called instead of the slow version, with the following arguments:
144  * receiver := the {embedder_object} from above
145  * param := 42
146  *
147  * Currently only void return types are supported.
148  * Currently supported argument types:
149  * - pointer to an embedder type
150  * - bool
151  * - int32_t
152  * - uint32_t
153  * To be supported types:
154  * - int64_t
155  * - uint64_t
156  * - float32_t
157  * - float64_t
158  * - arrays of C types
159  * - arrays of embedder types
160  */
161 
162 #ifndef INCLUDE_V8_FAST_API_CALLS_H_
163 #define INCLUDE_V8_FAST_API_CALLS_H_
164 
165 #include <stddef.h>
166 #include <stdint.h>
167 
168 #include "v8config.h" // NOLINT(build/include_directory)
169 
170 namespace v8 {
171 
172 class CTypeInfo {
173  public:
174  enum class Type : char {
175  kVoid,
176  kBool,
177  kInt32,
178  kUint32,
179  kInt64,
180  kUint64,
181  kFloat32,
182  kFloat64,
184  };
185 
186  enum class ArgFlags : uint8_t {
187  kNone = 0,
188  kIsArrayBit = 1 << 0, // This argument is first in an array of values.
189  };
190 
191  static CTypeInfo FromWrapperType(const void* wrapper_type_info,
192  ArgFlags flags = ArgFlags::kNone) {
193  uintptr_t wrapper_type_info_ptr =
194  reinterpret_cast<uintptr_t>(wrapper_type_info);
195  // Check that the lower kIsWrapperTypeBit bits are 0's.
196  CHECK_EQ(
197  wrapper_type_info_ptr & ~(static_cast<uintptr_t>(~0)
198  << static_cast<uintptr_t>(kIsWrapperTypeBit)),
199  0u);
200  // TODO(mslekova): Refactor the manual bit manipulations to use
201  // PointerWithPayload instead.
202  return CTypeInfo(wrapper_type_info_ptr | static_cast<int>(flags) |
203  kIsWrapperTypeBit);
204  }
205 
206  static constexpr CTypeInfo FromCType(Type ctype,
207  ArgFlags flags = ArgFlags::kNone) {
208  // ctype cannot be Type::kUnwrappedApiObject.
209  return CTypeInfo(
210  ((static_cast<uintptr_t>(ctype) << kTypeOffset) & kTypeMask) |
211  static_cast<int>(flags));
212  }
213 
214  const void* GetWrapperInfo() const;
215 
216  constexpr Type GetType() const {
217  if (payload_ & kIsWrapperTypeBit) {
219  }
220  return static_cast<Type>((payload_ & kTypeMask) >> kTypeOffset);
221  }
222 
223  constexpr bool IsArray() const {
224  return payload_ & static_cast<int>(ArgFlags::kIsArrayBit);
225  }
226 
227  private:
228  explicit constexpr CTypeInfo(uintptr_t payload) : payload_(payload) {}
229 
230  // That must be the last bit after ArgFlags.
231  static constexpr uintptr_t kIsWrapperTypeBit = 1 << 1;
232  static constexpr uintptr_t kWrapperTypeInfoMask = static_cast<uintptr_t>(~0)
233  << 2;
234 
235  static constexpr unsigned int kTypeOffset = kIsWrapperTypeBit;
236  static constexpr unsigned int kTypeSize = 8 - kTypeOffset;
237  static constexpr uintptr_t kTypeMask =
238  (~(static_cast<uintptr_t>(~0) << kTypeSize)) << kTypeOffset;
239 
240  const uintptr_t payload_;
241 };
242 
244  public:
245  virtual const CTypeInfo& ReturnInfo() const = 0;
246  virtual unsigned int ArgumentCount() const = 0;
247  virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0;
248 };
249 
250 template <typename T>
252  public:
253  static const void* GetTypeInfo() {
254  static_assert(sizeof(T) != sizeof(T),
255  "WrapperTraits must be specialized for this type.");
256  return nullptr;
257  }
258 };
259 
260 namespace internal {
261 
262 template <typename T>
263 struct GetCType {
264  static_assert(sizeof(T) != sizeof(T), "Unsupported CType");
265 };
266 
267 #define SPECIALIZE_GET_C_TYPE_FOR(ctype, ctypeinfo)
268  template <>
269  struct GetCType<ctype> {
270  static constexpr CTypeInfo Get() {
271  return CTypeInfo::FromCType(CTypeInfo::Type::ctypeinfo);
272  }
273  };
274 
275 #define SUPPORTED_C_TYPES(V)
276  V(void, kVoid)
277  V(bool, kBool)
278  V(int32_t, kInt32)
279  V(uint32_t, kUint32)
280  V(int64_t, kInt64)
281  V(uint64_t, kUint64)
282  V(float, kFloat32)
283  V(double, kFloat64)
284 
286 
287 template <typename T, typename = void>
289 
290 template <typename T>
291 struct EnableIfHasWrapperTypeInfo<T, decltype(WrapperTraits<T>::GetTypeInfo(),
292  void())> {
293  typedef void type;
294 };
295 
296 // T* where T is a primitive (array of primitives).
297 template <typename T, typename = void>
299  static constexpr CTypeInfo Get() {
300  return CTypeInfo::FromCType(GetCType<T>::Get().GetType(),
302  }
303 };
304 
305 // T* where T is an API object.
306 template <typename T>
308  static constexpr CTypeInfo Get() {
309  return CTypeInfo::FromWrapperType(WrapperTraits<T>::GetTypeInfo());
310  }
311 };
312 
313 // T** where T is a primitive. Not allowed.
314 template <typename T, typename = void>
316  static_assert(sizeof(T**) != sizeof(T**), "Unsupported type");
317 };
318 
319 // T** where T is an API object (array of API objects).
320 template <typename T>
322  T, typename EnableIfHasWrapperTypeInfo<T>::type> {
323  static constexpr CTypeInfo Get() {
324  return CTypeInfo::FromWrapperType(WrapperTraits<T>::GetTypeInfo(),
326  }
327 };
328 
329 template <typename T>
330 struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {};
331 
332 template <typename T>
333 struct GetCType<T*> : public GetCTypePointerImpl<T> {};
334 
335 template <typename R, typename... Args>
337  public:
339  : return_info_(internal::GetCType<R>::Get()),
340  arg_count_(sizeof...(Args)),
341  arg_info_{internal::GetCType<Args>::Get()...} {
342  static_assert(
343  internal::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
344  "Only void return types are currently supported.");
345  }
346 
347  const CTypeInfo& ReturnInfo() const override { return return_info_; }
348  unsigned int ArgumentCount() const override { return arg_count_; }
349  const CTypeInfo& ArgumentInfo(unsigned int index) const override {
350  CHECK_LT(index, ArgumentCount());
351  return arg_info_[index];
352  }
353 
354  private:
355  CTypeInfo return_info_;
356  const unsigned int arg_count_;
357  CTypeInfo arg_info_[sizeof...(Args)];
358 };
359 
360 } // namespace internal
361 
363  public:
364  constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
365 
366  const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
367 
368  const CTypeInfo& ArgumentInfo(unsigned int index) const {
369  return type_info_->ArgumentInfo(index);
370  }
371 
372  unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
373 
374  const void* GetAddress() const { return address_; }
375  const CFunctionInfo* GetTypeInfo() const { return type_info_; }
376 
377  template <typename F>
378  static CFunction Make(F* func) {
379  return ArgUnwrap<F*>::Make(func);
380  }
381 
382  private:
383  const void* address_;
384  const CFunctionInfo* type_info_;
385 
386  CFunction(const void* address, const CFunctionInfo* type_info);
387 
388  template <typename R, typename... Args>
389  static CFunctionInfo* GetCFunctionInfo() {
390  static internal::CFunctionInfoImpl<R, Args...> instance;
391  return &instance;
392  }
393 
394  template <typename F>
395  class ArgUnwrap {
396  static_assert(sizeof(F) != sizeof(F),
397  "CFunction must be created from a function pointer.");
398  };
399 
400  template <typename R, typename... Args>
401  class ArgUnwrap<R (*)(Args...)> {
402  public:
403  static CFunction Make(R (*func)(Args...)) {
404  return CFunction(reinterpret_cast<const void*>(func),
405  GetCFunctionInfo<R, Args...>());
406  }
407  };
408 };
409 
410 } // namespace v8
411 
412 #endif // INCLUDE_V8_FAST_API_CALLS_H_