v8  9.0.257(node16.0.0)
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  * By design, fast calls are limited by the following requirements, which
22  * the embedder should enforce themselves:
23  * - they should not allocate on the JS heap;
24  * - they should not trigger JS execution.
25  * To enforce them, the embedder could use the existing
26  * v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to
27  * Blink's NoAllocationScope:
28  * https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16
29  *
30  * Due to these limitations, it's not directly possible to report errors by
31  * throwing a JS exception or to otherwise do an allocation. There is an
32  * alternative way of creating fast calls that supports falling back to the
33  * slow call and then performing the necessary allocation. When one creates
34  * the fast method by using CFunction::MakeWithFallbackSupport instead of
35  * CFunction::Make, the fast callback gets as last parameter an output variable,
36  * through which it can request falling back to the slow call. So one might
37  * declare their method like:
38  *
39  * \code
40  * void FastMethodWithFallback(int param, FastApiCallbackOptions& options);
41  * \endcode
42  *
43  * If the callback wants to signal an error condition or to perform an
44  * allocation, it must set options.fallback to true and do an early return from
45  * the fast method. Then V8 checks the value of options.fallback and if it's
46  * true, falls back to executing the SlowCallback, which is capable of reporting
47  * the error (either by throwing a JS exception or logging to the console) or
48  * doing the allocation. It's the embedder's responsibility to ensure that the
49  * fast callback is idempotent up to the point where error and fallback
50  * conditions are checked, because otherwise executing the slow callback might
51  * produce visible side-effects twice.
52  *
53  * An example for custom embedder type support might employ a way to wrap/
54  * unwrap various C++ types in JSObject instances, e.g:
55  *
56  * \code
57  *
58  * // Helper method with a check for field count.
59  * template <typename T, int offset>
60  * inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
61  * assert(offset < wrapper->InternalFieldCount());
62  * return reinterpret_cast<T*>(
63  * wrapper->GetAlignedPointerFromInternalField(offset));
64  * }
65  *
66  * class CustomEmbedderType {
67  * public:
68  * // Returns the raw C object from a wrapper JS object.
69  * static CustomEmbedderType* Unwrap(v8::Local<v8::Object> wrapper) {
70  * return GetInternalField<CustomEmbedderType,
71  * kV8EmbedderWrapperObjectIndex>(wrapper);
72  * }
73  * static void FastMethod(v8::ApiObject receiver_obj, int param) {
74  * v8::Object* v8_object = reinterpret_cast<v8::Object*>(&api_object);
75  * CustomEmbedderType* receiver = static_cast<CustomEmbedderType*>(
76  * receiver_obj->GetAlignedPointerFromInternalField(
77  * kV8EmbedderWrapperObjectIndex));
78  *
79  * // Type checks are already done by the optimized code.
80  * // Then call some performance-critical method like:
81  * // receiver->Method(param);
82  * }
83  *
84  * static void SlowMethod(
85  * const v8::FunctionCallbackInfo<v8::Value>& info) {
86  * v8::Local<v8::Object> instance =
87  * v8::Local<v8::Object>::Cast(info.Holder());
88  * CustomEmbedderType* receiver = Unwrap(instance);
89  * // TODO: Do type checks and extract {param}.
90  * receiver->Method(param);
91  * }
92  * };
93  *
94  * // TODO(mslekova): Clean-up these constants
95  * // The constants kV8EmbedderWrapperTypeIndex and
96  * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info
97  * // struct and the native object, when expressed as internal field indices
98  * // within a JSObject. The existance of this helper function assumes that
99  * // all embedder objects have their JSObject-side type info at the same
100  * // offset, but this is not a limitation of the API itself. For a detailed
101  * // use case, see the third example.
102  * static constexpr int kV8EmbedderWrapperTypeIndex = 0;
103  * static constexpr int kV8EmbedderWrapperObjectIndex = 1;
104  *
105  * // The following setup function can be templatized based on
106  * // the {embedder_object} argument.
107  * void SetupCustomEmbedderObject(v8::Isolate* isolate,
108  * v8::Local<v8::Context> context,
109  * CustomEmbedderType* embedder_object) {
110  * isolate->set_embedder_wrapper_type_index(
111  * kV8EmbedderWrapperTypeIndex);
112  * isolate->set_embedder_wrapper_object_index(
113  * kV8EmbedderWrapperObjectIndex);
114  *
115  * v8::CFunction c_func =
116  * MakeV8CFunction(CustomEmbedderType::FastMethod);
117  *
118  * Local<v8::FunctionTemplate> method_template =
119  * v8::FunctionTemplate::New(
120  * isolate, CustomEmbedderType::SlowMethod, v8::Local<v8::Value>(),
121  * v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
122  * v8::SideEffectType::kHasSideEffect, &c_func);
123  *
124  * v8::Local<v8::ObjectTemplate> object_template =
125  * v8::ObjectTemplate::New(isolate);
126  * object_template->SetInternalFieldCount(
127  * kV8EmbedderWrapperObjectIndex + 1);
128  * object_template->Set(isolate, "method", method_template);
129  *
130  * // Instantiate the wrapper JS object.
131  * v8::Local<v8::Object> object =
132  * object_template->NewInstance(context).ToLocalChecked();
133  * object->SetAlignedPointerInInternalField(
134  * kV8EmbedderWrapperObjectIndex,
135  * reinterpret_cast<void*>(embedder_object));
136  *
137  * // TODO: Expose {object} where it's necessary.
138  * }
139  * \endcode
140  *
141  * For instance if {object} is exposed via a global "obj" variable,
142  * one could write in JS:
143  * function hot_func() {
144  * obj.method(42);
145  * }
146  * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod
147  * will be called instead of the slow version, with the following arguments:
148  * receiver := the {embedder_object} from above
149  * param := 42
150  *
151  * Currently supported return types:
152  * - void
153  * - bool
154  * - int32_t
155  * - uint32_t
156  * - float32_t
157  * - float64_t
158  * Currently supported argument types:
159  * - pointer to an embedder type
160  * - bool
161  * - int32_t
162  * - uint32_t
163  * - int64_t
164  * - uint64_t
165  * - float32_t
166  * - float64_t
167  *
168  * The 64-bit integer types currently have the IDL (unsigned) long long
169  * semantics: https://heycam.github.io/webidl/#abstract-opdef-converttoint
170  * In the future we'll extend the API to also provide conversions from/to
171  * BigInt to preserve full precision.
172  * The floating point types currently have the IDL (unrestricted) semantics,
173  * which is the only one used by WebGL. We plan to add support also for
174  * restricted floats/doubles, similarly to the BigInt conversion policies.
175  * We also differ from the specific NaN bit pattern that WebIDL prescribes
176  * (https://heycam.github.io/webidl/#es-unrestricted-float) in that Blink
177  * passes NaN values as-is, i.e. doesn't normalize them.
178  *
179  * To be supported types:
180  * - arrays of C types
181  * - arrays of embedder types
182  */
183 
184 #ifndef INCLUDE_V8_FAST_API_CALLS_H_
185 #define INCLUDE_V8_FAST_API_CALLS_H_
186 
187 #include <stddef.h>
188 #include <stdint.h>
189 
190 #include "v8config.h" // NOLINT(build/include_directory)
191 
192 namespace v8 {
193 
194 class CTypeInfo {
195  public:
196  enum class Type : uint8_t {
197  kVoid,
198  kBool,
199  kInt32,
200  kUint32,
201  kInt64,
202  kUint64,
203  kFloat32,
204  kFloat64,
205  kV8Value,
206  };
207 
208  // kCallbackOptionsType and kInvalidType are not part of the Type enum
209  // because they are only used internally. Use values 255 and 254 that
210  // are larger than any valid Type enum.
211  static constexpr Type kCallbackOptionsType = Type(255);
212  static constexpr Type kInvalidType = Type(254);
213 
214  enum class ArgFlags : uint8_t {
215  kNone = 0,
216  };
217 
218  explicit constexpr CTypeInfo(Type type, ArgFlags flags = ArgFlags::kNone)
219  : type_(type), flags_(flags) {}
220 
221  constexpr Type GetType() const { return type_; }
222 
223  constexpr ArgFlags GetFlags() const { return flags_; }
224 
225  static const CTypeInfo& Invalid() {
226  static CTypeInfo invalid = CTypeInfo(kInvalidType);
227  return invalid;
228  }
229 
230  private:
231  Type type_;
232  ArgFlags flags_;
233 };
234 
236  public:
237  virtual const CTypeInfo& ReturnInfo() const = 0;
238  virtual unsigned int ArgumentCount() const = 0;
239  virtual const CTypeInfo& ArgumentInfo(unsigned int index) const = 0;
240  virtual bool HasOptions() const = 0;
241 };
242 
243 struct ApiObject {
244  uintptr_t address;
245 };
246 
247 /**
248  * A struct which may be passed to a fast call callback, like so:
249  * \code
250  * void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
251  * \endcode
252  */
254  /**
255  * If the callback wants to signal an error condition or to perform an
256  * allocation, it must set options.fallback to true and do an early return
257  * from the fast method. Then V8 checks the value of options.fallback and if
258  * it's true, falls back to executing the SlowCallback, which is capable of
259  * reporting the error (either by throwing a JS exception or logging to the
260  * console) or doing the allocation. It's the embedder's responsibility to
261  * ensure that the fast callback is idempotent up to the point where error and
262  * fallback conditions are checked, because otherwise executing the slow
263  * callback might produce visible side-effects twice.
264  */
265  bool fallback;
266 
267  /**
268  * The `data` passed to the FunctionTemplate constructor, or `undefined`.
269  */
271 };
272 
273 namespace internal {
274 
275 template <typename T>
276 struct GetCType;
277 
278 #define SPECIALIZE_GET_C_TYPE_FOR(ctype, ctypeinfo)
279  template <>
280  struct GetCType<ctype> {
281  static constexpr CTypeInfo Get() {
282  return CTypeInfo(CTypeInfo::Type::ctypeinfo);
283  }
284  };
285 
286 #define SUPPORTED_C_TYPES(V)
287  V(void, kVoid)
288  V(bool, kBool)
289  V(int32_t, kInt32)
290  V(uint32_t, kUint32)
291  V(int64_t, kInt64)
292  V(uint64_t, kUint64)
293  V(float, kFloat32)
294  V(double, kFloat64)
295  V(ApiObject, kV8Value)
296 
298 
299 template <>
300 struct GetCType<FastApiCallbackOptions&> {
301  static constexpr CTypeInfo Get() {
303  }
304 };
305 
306 // Helper to count the number of occurances of `T` in `List`
307 template <typename T, typename... List>
308 struct count : std::integral_constant<int, 0> {};
309 template <typename T, typename... Args>
310 struct count<T, T, Args...>
311  : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
312 template <typename T, typename U, typename... Args>
313 struct count<T, U, Args...> : count<T, Args...> {};
314 
315 template <typename R, typename... Args>
317  public:
318  static constexpr int kOptionsArgCount =
319  count<FastApiCallbackOptions&, Args...>();
320  static constexpr int kReceiverCount = 1;
322  : return_info_(internal::GetCType<R>::Get()),
323  arg_count_(sizeof...(Args) - kOptionsArgCount),
324  arg_info_{internal::GetCType<Args>::Get()...} {
325  static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
326  "Only one options parameter is supported.");
327  static_assert(sizeof...(Args) >= kOptionsArgCount + kReceiverCount,
328  "The receiver or the fallback argument is missing.");
329  constexpr CTypeInfo::Type type = internal::GetCType<R>::Get().GetType();
330  static_assert(type == CTypeInfo::Type::kVoid ||
331  type == CTypeInfo::Type::kBool ||
332  type == CTypeInfo::Type::kInt32 ||
333  type == CTypeInfo::Type::kUint32 ||
334  type == CTypeInfo::Type::kFloat32 ||
336  "64-bit int and api object values are not currently "
337  "supported return types.");
338  }
339 
340  const CTypeInfo& ReturnInfo() const override { return return_info_; }
341  unsigned int ArgumentCount() const override { return arg_count_; }
342  const CTypeInfo& ArgumentInfo(unsigned int index) const override {
343  if (index >= ArgumentCount()) {
344  return CTypeInfo::Invalid();
345  }
346  return arg_info_[index];
347  }
348  bool HasOptions() const override { return kOptionsArgCount == 1; }
349 
350  private:
351  const CTypeInfo return_info_;
352  const unsigned int arg_count_;
353  const CTypeInfo arg_info_[sizeof...(Args)];
354 };
355 
356 } // namespace internal
357 
359  public:
360  constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
361 
362  const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
363 
364  const CTypeInfo& ArgumentInfo(unsigned int index) const {
365  return type_info_->ArgumentInfo(index);
366  }
367 
368  unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
369 
370  const void* GetAddress() const { return address_; }
371  const CFunctionInfo* GetTypeInfo() const { return type_info_; }
372 
373  template <typename F>
374  static CFunction Make(F* func) {
375  return ArgUnwrap<F*>::Make(func);
376  }
377 
378  template <typename F>
379  V8_DEPRECATED("Use CFunction::Make instead.")
381  return ArgUnwrap<F*>::Make(func);
382  }
383 
384  template <typename F>
385  static CFunction Make(F* func, const CFunctionInfo* type_info) {
386  return CFunction(reinterpret_cast<const void*>(func), type_info);
387  }
388 
389  private:
390  const void* address_;
391  const CFunctionInfo* type_info_;
392 
393  CFunction(const void* address, const CFunctionInfo* type_info);
394 
395  template <typename R, typename... Args>
396  static CFunctionInfo* GetCFunctionInfo() {
397  static internal::CFunctionInfoImpl<R, Args...> instance;
398  return &instance;
399  }
400 
401  template <typename F>
402  class ArgUnwrap {
403  static_assert(sizeof(F) != sizeof(F),
404  "CFunction must be created from a function pointer.");
405  };
406 
407  template <typename R, typename... Args>
408  class ArgUnwrap<R (*)(Args...)> {
409  public:
410  static CFunction Make(R (*func)(Args...)) {
411  return CFunction(reinterpret_cast<const void*>(func),
412  GetCFunctionInfo<R, Args...>());
413  }
414  };
415 };
416 
417 } // namespace v8
418 
419 #endif // INCLUDE_V8_FAST_API_CALLS_H_
v8::CFunctionInfo::ArgumentInfo
virtual const CTypeInfo & ArgumentInfo(unsigned int index) const =0
v8::CFunctionInfo::ReturnInfo
virtual const CTypeInfo & ReturnInfo() const =0
v8::CTypeInfo::Type::kUint64
@ kUint64
v8::CTypeInfo::Type::kFloat64
@ kFloat64
v8::FastApiCallbackOptions::data
const ApiObject data
Definition: v8-fast-api-calls.h:270
v8::CTypeInfo::CTypeInfo
constexpr CTypeInfo(Type type, ArgFlags flags=ArgFlags::kNone)
Definition: v8-fast-api-calls.h:218
v8::CFunction::Make
static CFunction Make(F *func)
Definition: v8-fast-api-calls.h:374
v8::ApiObject
Definition: v8-fast-api-calls.h:243
v8::CFunction::Make
static CFunction Make(F *func, const CFunctionInfo *type_info)
Definition: v8-fast-api-calls.h:385
v8::internal::CFunctionInfoImpl::ArgumentCount
unsigned int ArgumentCount() const override
Definition: v8-fast-api-calls.h:341
SUPPORTED_C_TYPES
#define SUPPORTED_C_TYPES(V)
Definition: v8-fast-api-calls.h:286
v8::CTypeInfo::Type::kVoid
@ kVoid
v8::ApiObject::address
uintptr_t address
Definition: v8-fast-api-calls.h:244
v8::CFunctionInfo::HasOptions
virtual bool HasOptions() const =0
v8::CTypeInfo::Invalid
static const CTypeInfo & Invalid()
Definition: v8-fast-api-calls.h:225
v8::FastApiCallbackOptions::fallback
bool fallback
Definition: v8-fast-api-calls.h:265
v8::internal::CFunctionInfoImpl
Definition: v8-fast-api-calls.h:316
v8::internal::CFunctionInfoImpl::ArgumentInfo
const CTypeInfo & ArgumentInfo(unsigned int index) const override
Definition: v8-fast-api-calls.h:342
v8::CTypeInfo::Type::kFloat32
@ kFloat32
v8::CTypeInfo::Type::kInt64
@ kInt64
v8::CTypeInfo::Type::kV8Value
@ kV8Value
v8::CTypeInfo::Type
Type
Definition: v8-fast-api-calls.h:196
v8::CFunction::CFunction
constexpr CFunction()
Definition: v8-fast-api-calls.h:360
v8::internal::count
Definition: v8-fast-api-calls.h:308
v8::CFunction::ReturnInfo
const CTypeInfo & ReturnInfo() const
Definition: v8-fast-api-calls.h:362
V8_EXPORT
#define V8_EXPORT
Definition: v8config.h:512
v8::internal::CFunctionInfoImpl::CFunctionInfoImpl
CFunctionInfoImpl()
Definition: v8-fast-api-calls.h:321
v8::CFunction::MakeWithFallbackSupport
static CFunction MakeWithFallbackSupport(F *func)
Definition: v8-fast-api-calls.h:380
v8::internal
Definition: v8-cppgc.h:26
v8::CTypeInfo::Type::kUint32
@ kUint32
v8::CTypeInfo::Type::kBool
@ kBool
v8::CFunction::GetAddress
const void * GetAddress() const
Definition: v8-fast-api-calls.h:370
v8
Definition: libplatform.h:15
v8::CTypeInfo::GetFlags
constexpr ArgFlags GetFlags() const
Definition: v8-fast-api-calls.h:223
v8::internal::CFunctionInfoImpl::kReceiverCount
static constexpr int kReceiverCount
Definition: v8-fast-api-calls.h:320
v8::CTypeInfo::ArgFlags
ArgFlags
Definition: v8-fast-api-calls.h:214
v8::internal::GetCType< FastApiCallbackOptions & >::Get
static constexpr CTypeInfo Get()
Definition: v8-fast-api-calls.h:301
V8_DEPRECATED
#define V8_DEPRECATED(message)
Definition: v8config.h:417
v8::CFunctionInfo::ArgumentCount
virtual unsigned int ArgumentCount() const =0
v8::internal::CFunctionInfoImpl::kOptionsArgCount
static constexpr int kOptionsArgCount
Definition: v8-fast-api-calls.h:318
v8::CFunction::ArgumentInfo
const CTypeInfo & ArgumentInfo(unsigned int index) const
Definition: v8-fast-api-calls.h:364
v8::CFunction::ArgumentCount
unsigned int ArgumentCount() const
Definition: v8-fast-api-calls.h:368
v8::internal::CFunctionInfoImpl::ReturnInfo
const CTypeInfo & ReturnInfo() const override
Definition: v8-fast-api-calls.h:340
v8::CTypeInfo::Type::kInt32
@ kInt32
v8::CTypeInfo::kInvalidType
static constexpr Type kInvalidType
Definition: v8-fast-api-calls.h:212
v8::CTypeInfo::kCallbackOptionsType
static constexpr Type kCallbackOptionsType
Definition: v8-fast-api-calls.h:211
v8::CFunctionInfo
Definition: v8-fast-api-calls.h:235
v8::FastApiCallbackOptions
Definition: v8-fast-api-calls.h:253
v8::CFunction
Definition: v8-fast-api-calls.h:358
v8::CTypeInfo::ArgFlags::kNone
@ kNone
v8::CTypeInfo
Definition: v8-fast-api-calls.h:194
v8::internal::CFunctionInfoImpl::HasOptions
bool HasOptions() const override
Definition: v8-fast-api-calls.h:348
v8::CTypeInfo::GetType
constexpr Type GetType() const
Definition: v8-fast-api-calls.h:221
v8::CFunction::GetTypeInfo
const CFunctionInfo * GetTypeInfo() const
Definition: v8-fast-api-calls.h:371