1 // Copyright 2021 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 #include "src/d8/d8.h"
6
7 #include "include/v8-fast-api-calls.h"
8 #include "include/v8-template.h"
9 #include "src/api/api-inl.h"
10
11 // This file exposes a d8.test.fast_c_api object, which adds testing facility
12 // for writing mjsunit tests that exercise fast API calls. The fast_c_api object
13 // contains an `add_all` method with the following signature:
14 // double add_all(bool /*should_fallback*/, int32_t, uint32_t,
15 // int64_t, uint64_t, float, double), that is wired as a "fast API" method.
16 // The fast_c_api object also supports querying the number of fast/slow calls
17 // and resetting these counters.
18
19 // Make sure to sync the following with src/compiler/globals.h.
20 #if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
21 #define V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
22 #endif
23
24 namespace v8 {
25 namespace {
26
27 #define CHECK_SELF_OR_FALLBACK(return_value) \
28 if (!self) { \
29 options.fallback = 1; \
30 return return_value; \
31 }
32
33 #define CHECK_SELF_OR_THROW() \
34 if (!self) { \
35 args.GetIsolate()->ThrowError( \
36 "This method is not defined on objects inheriting from FastCAPI."); \
37 return; \
38 }
39
40 class FastCApiObject {
41 public:
42 static FastCApiObject& instance();
43
44 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllFastCallbackPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg_i32,AnyCType arg_u32,AnyCType arg_i64,AnyCType arg_u64,AnyCType arg_f32,AnyCType arg_f64,AnyCType options)45 static AnyCType AddAllFastCallbackPatch(AnyCType receiver,
46 AnyCType should_fallback,
47 AnyCType arg_i32, AnyCType arg_u32,
48 AnyCType arg_i64, AnyCType arg_u64,
49 AnyCType arg_f32, AnyCType arg_f64,
50 AnyCType options) {
51 AnyCType ret;
52 ret.double_value = AddAllFastCallback(
53 receiver.object_value, should_fallback.bool_value, arg_i32.int32_value,
54 arg_u32.uint32_value, arg_i64.int64_value, arg_u64.uint64_value,
55 arg_f32.float_value, arg_f64.double_value, *options.options_value);
56 return ret;
57 }
58
59 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllFastCallback(Local<Object> receiver,bool should_fallback,int32_t arg_i32,uint32_t arg_u32,int64_t arg_i64,uint64_t arg_u64,float arg_f32,double arg_f64,FastApiCallbackOptions & options)60 static double AddAllFastCallback(Local<Object> receiver, bool should_fallback,
61 int32_t arg_i32, uint32_t arg_u32,
62 int64_t arg_i64, uint64_t arg_u64,
63 float arg_f32, double arg_f64,
64 FastApiCallbackOptions& options) {
65 FastCApiObject* self = UnwrapObject(receiver);
66 CHECK_SELF_OR_FALLBACK(0);
67 self->fast_call_count_++;
68
69 if (should_fallback) {
70 options.fallback = true;
71 return 0;
72 } else {
73 options.fallback = false;
74 }
75
76 return static_cast<double>(arg_i32) + static_cast<double>(arg_u32) +
77 static_cast<double>(arg_i64) + static_cast<double>(arg_u64) +
78 static_cast<double>(arg_f32) + arg_f64;
79 }
80
81 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllFastCallbackNoOptionsPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg_i32,AnyCType arg_u32,AnyCType arg_i64,AnyCType arg_u64,AnyCType arg_f32,AnyCType arg_f64)82 static AnyCType AddAllFastCallbackNoOptionsPatch(
83 AnyCType receiver, AnyCType should_fallback, AnyCType arg_i32,
84 AnyCType arg_u32, AnyCType arg_i64, AnyCType arg_u64, AnyCType arg_f32,
85 AnyCType arg_f64) {
86 AnyCType ret;
87 ret.double_value = AddAllFastCallbackNoOptions(
88 receiver.object_value, should_fallback.bool_value, arg_i32.int32_value,
89 arg_u32.uint32_value, arg_i64.int64_value, arg_u64.uint64_value,
90 arg_f32.float_value, arg_f64.double_value);
91 return ret;
92 }
93 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllFastCallbackNoOptions(Local<Object> receiver,bool should_fallback,int32_t arg_i32,uint32_t arg_u32,int64_t arg_i64,uint64_t arg_u64,float arg_f32,double arg_f64)94 static double AddAllFastCallbackNoOptions(Local<Object> receiver,
95 bool should_fallback,
96 int32_t arg_i32, uint32_t arg_u32,
97 int64_t arg_i64, uint64_t arg_u64,
98 float arg_f32, double arg_f64) {
99 FastCApiObject* self;
100
101 // For Wasm call, we don't pass FastCApiObject as the receiver, so we need
102 // to retrieve the FastCApiObject instance from a static variable.
103 if (Utils::OpenHandle(*receiver)->IsJSGlobalProxy() ||
104 Utils::OpenHandle(*receiver)->IsUndefined()) {
105 // Note: FastCApiObject::instance() returns the reference of an object
106 // allocated in thread-local storage, its value cannot be stored in a
107 // static variable here.
108 self = &FastCApiObject::instance();
109 } else {
110 // Fuzzing code can call this function from JS; in this case the receiver
111 // should be a FastCApiObject.
112 self = UnwrapObject(receiver);
113 CHECK_NOT_NULL(self);
114 }
115 self->fast_call_count_++;
116
117 return static_cast<double>(arg_i32) + static_cast<double>(arg_u32) +
118 static_cast<double>(arg_i64) + static_cast<double>(arg_u64) +
119 static_cast<double>(arg_f32) + arg_f64;
120 }
121
AddAllSlowCallback(const FunctionCallbackInfo<Value> & args)122 static void AddAllSlowCallback(const FunctionCallbackInfo<Value>& args) {
123 Isolate* isolate = args.GetIsolate();
124
125 FastCApiObject* self = UnwrapObject(args.This());
126 CHECK_SELF_OR_THROW();
127 self->slow_call_count_++;
128
129 HandleScope handle_scope(isolate);
130
131 double sum = 0;
132 if (args.Length() > 1 && args[1]->IsNumber()) {
133 sum += args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
134 }
135 if (args.Length() > 2 && args[2]->IsNumber()) {
136 sum += args[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
137 }
138 if (args.Length() > 3 && args[3]->IsNumber()) {
139 sum += args[3]->IntegerValue(isolate->GetCurrentContext()).FromJust();
140 }
141 if (args.Length() > 4 && args[4]->IsNumber()) {
142 sum += args[4]->IntegerValue(isolate->GetCurrentContext()).FromJust();
143 }
144 if (args.Length() > 5 && args[5]->IsNumber()) {
145 sum += args[5]->NumberValue(isolate->GetCurrentContext()).FromJust();
146 } else {
147 sum += std::numeric_limits<double>::quiet_NaN();
148 }
149 if (args.Length() > 6 && args[6]->IsNumber()) {
150 sum += args[6]->NumberValue(isolate->GetCurrentContext()).FromJust();
151 } else {
152 sum += std::numeric_limits<double>::quiet_NaN();
153 }
154
155 args.GetReturnValue().Set(Number::New(isolate, sum));
156 }
157
158 #ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
159 typedef double Type;
160 #else
161 typedef int32_t Type;
162 #endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
163 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllSequenceFastCallbackPatch(AnyCType receiver,AnyCType should_fallback,AnyCType seq_arg,AnyCType options)164 static AnyCType AddAllSequenceFastCallbackPatch(AnyCType receiver,
165 AnyCType should_fallback,
166 AnyCType seq_arg,
167 AnyCType options) {
168 AnyCType ret;
169 #ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
170 ret.double_value = AddAllSequenceFastCallback(
171 receiver.object_value, should_fallback.bool_value,
172 seq_arg.sequence_value, *options.options_value);
173 #else
174 ret.int32_value = AddAllSequenceFastCallback(
175 receiver.object_value, should_fallback.bool_value,
176 seq_arg.sequence_value, *options.options_value);
177 #endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
178 return ret;
179 }
180 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAllSequenceFastCallback(Local<Object> receiver,bool should_fallback,Local<Array> seq_arg,FastApiCallbackOptions & options)181 static Type AddAllSequenceFastCallback(Local<Object> receiver,
182 bool should_fallback,
183 Local<Array> seq_arg,
184 FastApiCallbackOptions& options) {
185 FastCApiObject* self = UnwrapObject(receiver);
186 CHECK_SELF_OR_FALLBACK(0);
187 self->fast_call_count_++;
188
189 if (should_fallback) {
190 options.fallback = true;
191 return 0;
192 }
193
194 uint32_t length = seq_arg->Length();
195 if (length > 1024) {
196 options.fallback = true;
197 return 0;
198 }
199
200 Type buffer[1024];
201 bool result = TryToCopyAndConvertArrayToCppBuffer<
202 CTypeInfoBuilder<Type>::Build().GetId(), Type>(seq_arg, buffer, 1024);
203 if (!result) {
204 options.fallback = true;
205 return 0;
206 }
207 DCHECK_EQ(seq_arg->Length(), length);
208
209 Type sum = 0;
210 for (uint32_t i = 0; i < length; ++i) {
211 sum += buffer[i];
212 }
213
214 return sum;
215 }
AddAllSequenceSlowCallback(const FunctionCallbackInfo<Value> & args)216 static void AddAllSequenceSlowCallback(
217 const FunctionCallbackInfo<Value>& args) {
218 Isolate* isolate = args.GetIsolate();
219
220 FastCApiObject* self = UnwrapObject(args.This());
221 CHECK_SELF_OR_THROW();
222
223 HandleScope handle_scope(isolate);
224
225 if (args.Length() < 2) {
226 self->slow_call_count_++;
227 isolate->ThrowError("This method expects at least 2 arguments.");
228 return;
229 }
230 if (args[1]->IsTypedArray()) {
231 AddAllTypedArraySlowCallback(args);
232 return;
233 }
234 self->slow_call_count_++;
235 if (args[1]->IsUndefined()) {
236 Type dummy_result = 0;
237 args.GetReturnValue().Set(Number::New(isolate, dummy_result));
238 return;
239 }
240 if (!args[1]->IsArray()) {
241 isolate->ThrowError("This method expects an array as a second argument.");
242 return;
243 }
244
245 Local<Array> seq_arg = args[1].As<Array>();
246 uint32_t length = seq_arg->Length();
247 if (length > 1024) {
248 isolate->ThrowError(
249 "Invalid length of array, must be between 0 and 1024.");
250 return;
251 }
252
253 Type sum = 0;
254 for (uint32_t i = 0; i < length; ++i) {
255 v8::Local<v8::Value> element =
256 seq_arg
257 ->Get(isolate->GetCurrentContext(),
258 v8::Integer::NewFromUnsigned(isolate, i))
259 .ToLocalChecked();
260 if (element->IsNumber()) {
261 double value = element->ToNumber(isolate->GetCurrentContext())
262 .ToLocalChecked()
263 ->Value();
264 sum += value;
265 } else if (element->IsUndefined()) {
266 // Hole: ignore the element.
267 } else {
268 isolate->ThrowError("unexpected element type in JSArray");
269 return;
270 }
271 }
272 args.GetReturnValue().Set(Number::New(isolate, sum));
273 }
274 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
275 template <typename T>
276 static const FastApiTypedArray<T>* AnyCTypeToTypedArray(AnyCType arg);
277
278 template <>
AnyCTypeToTypedArray(AnyCType arg)279 const FastApiTypedArray<int32_t>* AnyCTypeToTypedArray<int32_t>(
280 AnyCType arg) {
281 return arg.int32_ta_value;
282 }
283 template <>
AnyCTypeToTypedArray(AnyCType arg)284 const FastApiTypedArray<uint32_t>* AnyCTypeToTypedArray<uint32_t>(
285 AnyCType arg) {
286 return arg.uint32_ta_value;
287 }
288 template <>
AnyCTypeToTypedArray(AnyCType arg)289 const FastApiTypedArray<int64_t>* AnyCTypeToTypedArray<int64_t>(
290 AnyCType arg) {
291 return arg.int64_ta_value;
292 }
293 template <>
AnyCTypeToTypedArray(AnyCType arg)294 const FastApiTypedArray<uint64_t>* AnyCTypeToTypedArray<uint64_t>(
295 AnyCType arg) {
296 return arg.uint64_ta_value;
297 }
298 template <>
AnyCTypeToTypedArray(AnyCType arg)299 const FastApiTypedArray<float>* AnyCTypeToTypedArray<float>(AnyCType arg) {
300 return arg.float_ta_value;
301 }
302 template <>
AnyCTypeToTypedArray(AnyCType arg)303 const FastApiTypedArray<double>* AnyCTypeToTypedArray<double>(AnyCType arg) {
304 return arg.double_ta_value;
305 }
306
307 template <typename T>
AddAllTypedArrayFastCallbackPatch(AnyCType receiver,AnyCType should_fallback,AnyCType typed_array_arg,AnyCType options)308 static AnyCType AddAllTypedArrayFastCallbackPatch(AnyCType receiver,
309 AnyCType should_fallback,
310 AnyCType typed_array_arg,
311 AnyCType options) {
312 AnyCType ret;
313 #ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
314 ret.double_value = AddAllTypedArrayFastCallback(
315 receiver.object_value, should_fallback.bool_value,
316 *AnyCTypeToTypedArray<T>(typed_array_arg), *options.options_value);
317 #else
318 ret.int32_value = AddAllTypedArrayFastCallback(
319 receiver.object_value, should_fallback.bool_value,
320 *AnyCTypeToTypedArray<T>(typed_array_arg), *options.options_value);
321 #endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
322 return ret;
323 }
324 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
325 template <typename T>
AddAllTypedArrayFastCallback(Local<Object> receiver,bool should_fallback,const FastApiTypedArray<T> & typed_array_arg,FastApiCallbackOptions & options)326 static Type AddAllTypedArrayFastCallback(
327 Local<Object> receiver, bool should_fallback,
328 const FastApiTypedArray<T>& typed_array_arg,
329 FastApiCallbackOptions& options) {
330 FastCApiObject* self = UnwrapObject(receiver);
331 CHECK_SELF_OR_FALLBACK(0);
332 self->fast_call_count_++;
333
334 if (should_fallback) {
335 options.fallback = true;
336 return 0;
337 }
338
339 T sum = 0;
340 for (unsigned i = 0; i < typed_array_arg.length(); ++i) {
341 sum += typed_array_arg.get(i);
342 }
343 return static_cast<Type>(sum);
344 }
AddAllTypedArraySlowCallback(const FunctionCallbackInfo<Value> & args)345 static void AddAllTypedArraySlowCallback(
346 const FunctionCallbackInfo<Value>& args) {
347 Isolate* isolate = args.GetIsolate();
348
349 FastCApiObject* self = UnwrapObject(args.This());
350 CHECK_SELF_OR_THROW();
351 self->slow_call_count_++;
352
353 HandleScope handle_scope(isolate);
354
355 if (args.Length() < 2) {
356 isolate->ThrowError("This method expects at least 2 arguments.");
357 return;
358 }
359 if (!args[1]->IsTypedArray()) {
360 isolate->ThrowError(
361 "This method expects a TypedArray as a second argument.");
362 return;
363 }
364
365 Local<TypedArray> typed_array_arg = args[1].As<TypedArray>();
366 size_t length = typed_array_arg->Length();
367
368 void* data = typed_array_arg->Buffer()->GetBackingStore()->Data();
369 if (typed_array_arg->IsInt32Array() || typed_array_arg->IsUint32Array() ||
370 typed_array_arg->IsBigInt64Array() ||
371 typed_array_arg->IsBigUint64Array()) {
372 int64_t sum = 0;
373 for (unsigned i = 0; i < length; ++i) {
374 if (typed_array_arg->IsInt32Array()) {
375 sum += static_cast<int32_t*>(data)[i];
376 } else if (typed_array_arg->IsUint32Array()) {
377 sum += static_cast<uint32_t*>(data)[i];
378 } else if (typed_array_arg->IsBigInt64Array()) {
379 sum += static_cast<int64_t*>(data)[i];
380 } else if (typed_array_arg->IsBigUint64Array()) {
381 sum += static_cast<uint64_t*>(data)[i];
382 }
383 }
384 args.GetReturnValue().Set(Number::New(isolate, sum));
385 } else if (typed_array_arg->IsFloat32Array() ||
386 typed_array_arg->IsFloat64Array()) {
387 double sum = 0;
388 for (unsigned i = 0; i < length; ++i) {
389 if (typed_array_arg->IsFloat32Array()) {
390 sum += static_cast<float*>(data)[i];
391 } else if (typed_array_arg->IsFloat64Array()) {
392 sum += static_cast<double*>(data)[i];
393 }
394 }
395 args.GetReturnValue().Set(Number::New(isolate, sum));
396 } else {
397 isolate->ThrowError("TypedArray type is not supported.");
398 return;
399 }
400 }
401
AddAllIntInvalidCallback(Local<Object> receiver,bool should_fallback,int32_t arg_i32,FastApiCallbackOptions & options)402 static int32_t AddAllIntInvalidCallback(Local<Object> receiver,
403 bool should_fallback, int32_t arg_i32,
404 FastApiCallbackOptions& options) {
405 // This should never be called
406 UNREACHABLE();
407 }
408
409 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
Add32BitIntFastCallbackPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg_i32,AnyCType arg_u32,AnyCType options)410 static AnyCType Add32BitIntFastCallbackPatch(AnyCType receiver,
411 AnyCType should_fallback,
412 AnyCType arg_i32,
413 AnyCType arg_u32,
414 AnyCType options) {
415 AnyCType ret;
416 ret.int32_value = Add32BitIntFastCallback(
417 receiver.object_value, should_fallback.bool_value, arg_i32.int32_value,
418 arg_u32.uint32_value, *options.options_value);
419 return ret;
420 }
421 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
422
Add32BitIntFastCallback(v8::Local<v8::Object> receiver,bool should_fallback,int32_t arg_i32,uint32_t arg_u32,FastApiCallbackOptions & options)423 static int Add32BitIntFastCallback(v8::Local<v8::Object> receiver,
424 bool should_fallback, int32_t arg_i32,
425 uint32_t arg_u32,
426 FastApiCallbackOptions& options) {
427 FastCApiObject* self = UnwrapObject(receiver);
428 CHECK_SELF_OR_FALLBACK(0);
429 self->fast_call_count_++;
430
431 if (should_fallback) {
432 options.fallback = true;
433 return 0;
434 }
435
436 return arg_i32 + arg_u32;
437 }
Add32BitIntSlowCallback(const FunctionCallbackInfo<Value> & args)438 static void Add32BitIntSlowCallback(const FunctionCallbackInfo<Value>& args) {
439 Isolate* isolate = args.GetIsolate();
440
441 FastCApiObject* self = UnwrapObject(args.This());
442 CHECK_SELF_OR_THROW();
443 self->slow_call_count_++;
444
445 HandleScope handle_scope(isolate);
446
447 double sum = 0;
448 if (args.Length() > 1 && args[1]->IsNumber()) {
449 sum += args[1]->Int32Value(isolate->GetCurrentContext()).FromJust();
450 }
451 if (args.Length() > 2 && args[2]->IsNumber()) {
452 sum += args[2]->Uint32Value(isolate->GetCurrentContext()).FromJust();
453 }
454
455 args.GetReturnValue().Set(Number::New(isolate, sum));
456 }
457
458 #ifdef V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
AddAll32BitIntFastCallback_8ArgsPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg1_i32,AnyCType arg2_i32,AnyCType arg3_i32,AnyCType arg4_u32,AnyCType arg5_u32,AnyCType arg6_u32,AnyCType arg7_u32,AnyCType arg8_u32,AnyCType options)459 static AnyCType AddAll32BitIntFastCallback_8ArgsPatch(
460 AnyCType receiver, AnyCType should_fallback, AnyCType arg1_i32,
461 AnyCType arg2_i32, AnyCType arg3_i32, AnyCType arg4_u32,
462 AnyCType arg5_u32, AnyCType arg6_u32, AnyCType arg7_u32,
463 AnyCType arg8_u32, AnyCType options) {
464 AnyCType ret;
465 ret.int32_value = AddAll32BitIntFastCallback_8Args(
466 receiver.object_value, should_fallback.bool_value, arg1_i32.int32_value,
467 arg2_i32.int32_value, arg3_i32.int32_value, arg4_u32.uint32_value,
468 arg5_u32.uint32_value, arg6_u32.uint32_value, arg7_u32.uint32_value,
469 arg8_u32.uint32_value, *options.options_value);
470 return ret;
471 }
AddAll32BitIntFastCallback_6ArgsPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg1_i32,AnyCType arg2_i32,AnyCType arg3_i32,AnyCType arg4_u32,AnyCType arg5_u32,AnyCType arg6_u32,AnyCType options)472 static AnyCType AddAll32BitIntFastCallback_6ArgsPatch(
473 AnyCType receiver, AnyCType should_fallback, AnyCType arg1_i32,
474 AnyCType arg2_i32, AnyCType arg3_i32, AnyCType arg4_u32,
475 AnyCType arg5_u32, AnyCType arg6_u32, AnyCType options) {
476 AnyCType ret;
477 ret.int32_value = AddAll32BitIntFastCallback_6Args(
478 receiver.object_value, should_fallback.bool_value, arg1_i32.int32_value,
479 arg2_i32.int32_value, arg3_i32.int32_value, arg4_u32.uint32_value,
480 arg5_u32.uint32_value, arg6_u32.uint32_value, *options.options_value);
481 return ret;
482 }
AddAll32BitIntFastCallback_5ArgsPatch(AnyCType receiver,AnyCType should_fallback,AnyCType arg1_i32,AnyCType arg2_i32,AnyCType arg3_i32,AnyCType arg4_u32,AnyCType arg5_u32,AnyCType options)483 static AnyCType AddAll32BitIntFastCallback_5ArgsPatch(
484 AnyCType receiver, AnyCType should_fallback, AnyCType arg1_i32,
485 AnyCType arg2_i32, AnyCType arg3_i32, AnyCType arg4_u32,
486 AnyCType arg5_u32, AnyCType options) {
487 AnyCType arg6;
488 arg6.uint32_value = 0;
489 return AddAll32BitIntFastCallback_6ArgsPatch(
490 receiver, should_fallback, arg1_i32, arg2_i32, arg3_i32, arg4_u32,
491 arg5_u32, arg6, options);
492 }
493 #endif // V8_USE_SIMULATOR_WITH_GENERIC_C_CALLS
494
AddAll32BitIntFastCallback_8Args(Local<Object> receiver,bool should_fallback,int32_t arg1_i32,int32_t arg2_i32,int32_t arg3_i32,uint32_t arg4_u32,uint32_t arg5_u32,uint32_t arg6_u32,uint32_t arg7_u32,uint32_t arg8_u32,FastApiCallbackOptions & options)495 static int AddAll32BitIntFastCallback_8Args(
496 Local<Object> receiver, bool should_fallback, int32_t arg1_i32,
497 int32_t arg2_i32, int32_t arg3_i32, uint32_t arg4_u32, uint32_t arg5_u32,
498 uint32_t arg6_u32, uint32_t arg7_u32, uint32_t arg8_u32,
499 FastApiCallbackOptions& options) {
500 FastCApiObject* self = UnwrapObject(receiver);
501 CHECK_SELF_OR_FALLBACK(0);
502 self->fast_call_count_++;
503
504 if (should_fallback) {
505 options.fallback = true;
506 return 0;
507 }
508
509 int64_t result = static_cast<int64_t>(arg1_i32) + arg2_i32 + arg3_i32 +
510 arg4_u32 + arg5_u32 + arg6_u32 + arg7_u32 + arg8_u32;
511 if (result > INT_MAX) return INT_MAX;
512 if (result < INT_MIN) return INT_MIN;
513 return static_cast<int>(result);
514 }
AddAll32BitIntFastCallback_6Args(Local<Object> receiver,bool should_fallback,int32_t arg1_i32,int32_t arg2_i32,int32_t arg3_i32,uint32_t arg4_u32,uint32_t arg5_u32,uint32_t arg6_u32,FastApiCallbackOptions & options)515 static int AddAll32BitIntFastCallback_6Args(
516 Local<Object> receiver, bool should_fallback, int32_t arg1_i32,
517 int32_t arg2_i32, int32_t arg3_i32, uint32_t arg4_u32, uint32_t arg5_u32,
518 uint32_t arg6_u32, FastApiCallbackOptions& options) {
519 FastCApiObject* self = UnwrapObject(receiver);
520 CHECK_SELF_OR_FALLBACK(0);
521 self->fast_call_count_++;
522
523 if (should_fallback) {
524 options.fallback = true;
525 return 0;
526 }
527
528 int64_t result = static_cast<int64_t>(arg1_i32) + arg2_i32 + arg3_i32 +
529 arg4_u32 + arg5_u32 + arg6_u32;
530 if (result > INT_MAX) return INT_MAX;
531 if (result < INT_MIN) return INT_MIN;
532 return static_cast<int>(result);
533 }
AddAll32BitIntFastCallback_5Args(Local<Object> receiver,bool should_fallback,int32_t arg1_i32,int32_t arg2_i32,int32_t arg3_i32,uint32_t arg4_u32,uint32_t arg5_u32,FastApiCallbackOptions & options)534 static int AddAll32BitIntFastCallback_5Args(
535 Local<Object> receiver, bool should_fallback, int32_t arg1_i32,
536 int32_t arg2_i32, int32_t arg3_i32, uint32_t arg4_u32, uint32_t arg5_u32,
537 FastApiCallbackOptions& options) {
538 return AddAll32BitIntFastCallback_6Args(receiver, should_fallback, arg1_i32,
539 arg2_i32, arg3_i32, arg4_u32,
540 arg5_u32, 0, options);
541 }
AddAll32BitIntSlowCallback(const FunctionCallbackInfo<Value> & args)542 static void AddAll32BitIntSlowCallback(
543 const FunctionCallbackInfo<Value>& args) {
544 Isolate* isolate = args.GetIsolate();
545
546 FastCApiObject* self = UnwrapObject(args.This());
547 CHECK_SELF_OR_THROW();
548 self->slow_call_count_++;
549
550 HandleScope handle_scope(isolate);
551
552 Local<Context> context = isolate->GetCurrentContext();
553 double sum = 0;
554 if (args.Length() > 1 && args[1]->IsNumber()) {
555 sum += args[1]->Int32Value(context).FromJust();
556 }
557 if (args.Length() > 2 && args[2]->IsNumber()) {
558 sum += args[2]->Int32Value(context).FromJust();
559 }
560 if (args.Length() > 3 && args[3]->IsNumber()) {
561 sum += args[3]->Int32Value(context).FromJust();
562 }
563 if (args.Length() > 4 && args[4]->IsNumber()) {
564 sum += args[4]->Uint32Value(context).FromJust();
565 }
566 if (args.Length() > 5 && args[5]->IsNumber()) {
567 sum += args[5]->Uint32Value(context).FromJust();
568 }
569 if (args.Length() > 6 && args[6]->IsNumber()) {
570 sum += args[6]->Uint32Value(context).FromJust();
571 }
572 if (args.Length() > 7 && args[7]->IsNumber() && args[8]->IsNumber()) {
573 sum += args[7]->Uint32Value(context).FromJust();
574 sum += args[8]->Uint32Value(context).FromJust();
575 }
576
577 args.GetReturnValue().Set(Number::New(isolate, sum));
578 }
579
IsFastCApiObjectFastCallback(v8::Local<v8::Object> receiver,bool should_fallback,v8::Local<v8::Value> arg,FastApiCallbackOptions & options)580 static bool IsFastCApiObjectFastCallback(v8::Local<v8::Object> receiver,
581 bool should_fallback,
582 v8::Local<v8::Value> arg,
583 FastApiCallbackOptions& options) {
584 FastCApiObject* self = UnwrapObject(receiver);
585 CHECK_SELF_OR_FALLBACK(false);
586 self->fast_call_count_++;
587
588 if (should_fallback) {
589 options.fallback = true;
590 return false;
591 }
592
593 if (!arg->IsObject()) {
594 return false;
595 }
596 Local<Object> object = arg.As<Object>();
597 if (!IsValidApiObject(object)) return false;
598
599 internal::Isolate* i_isolate =
600 internal::IsolateFromNeverReadOnlySpaceObject(
601 *reinterpret_cast<internal::Address*>(*object));
602 CHECK_NOT_NULL(i_isolate);
603 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
604 HandleScope handle_scope(isolate);
605 return PerIsolateData::Get(isolate)
606 ->GetTestApiObjectCtor()
607 ->IsLeafTemplateForApiObject(object);
608 }
IsFastCApiObjectSlowCallback(const FunctionCallbackInfo<Value> & args)609 static void IsFastCApiObjectSlowCallback(
610 const FunctionCallbackInfo<Value>& args) {
611 Isolate* isolate = args.GetIsolate();
612
613 FastCApiObject* self = UnwrapObject(args.This());
614 CHECK_SELF_OR_THROW();
615 self->slow_call_count_++;
616
617 HandleScope handle_scope(isolate);
618
619 bool result = false;
620 if (args.Length() < 2) {
621 args.GetIsolate()->ThrowError(
622 "is_valid_api_object should be called with 2 arguments");
623 return;
624 }
625 if (args[1]->IsObject()) {
626 Local<Object> object = args[1].As<Object>();
627 if (!IsValidApiObject(object)) {
628 result = false;
629 } else {
630 result = PerIsolateData::Get(args.GetIsolate())
631 ->GetTestApiObjectCtor()
632 ->IsLeafTemplateForApiObject(object);
633 }
634 }
635
636 args.GetReturnValue().Set(Boolean::New(isolate, result));
637 }
638
FastCallCount(const FunctionCallbackInfo<Value> & args)639 static void FastCallCount(const FunctionCallbackInfo<Value>& args) {
640 FastCApiObject* self = UnwrapObject(args.This());
641 CHECK_SELF_OR_THROW();
642 args.GetReturnValue().Set(
643 Number::New(args.GetIsolate(), self->fast_call_count()));
644 }
SlowCallCount(const FunctionCallbackInfo<Value> & args)645 static void SlowCallCount(const FunctionCallbackInfo<Value>& args) {
646 FastCApiObject* self = UnwrapObject(args.This());
647 CHECK_SELF_OR_THROW();
648 args.GetReturnValue().Set(
649 Number::New(args.GetIsolate(), self->slow_call_count()));
650 }
ResetCounts(const FunctionCallbackInfo<Value> & args)651 static void ResetCounts(const FunctionCallbackInfo<Value>& args) {
652 FastCApiObject* self = UnwrapObject(args.This());
653 CHECK_SELF_OR_THROW();
654 self->reset_counts();
655 args.GetReturnValue().Set(Undefined(args.GetIsolate()));
656 }
SupportsFPParams(const FunctionCallbackInfo<Value> & args)657 static void SupportsFPParams(const FunctionCallbackInfo<Value>& args) {
658 FastCApiObject* self = UnwrapObject(args.This());
659 CHECK_SELF_OR_THROW();
660 args.GetReturnValue().Set(self->supports_fp_params_);
661 }
662
fast_call_count() const663 int fast_call_count() const { return fast_call_count_; }
slow_call_count() const664 int slow_call_count() const { return slow_call_count_; }
reset_counts()665 void reset_counts() {
666 fast_call_count_ = 0;
667 slow_call_count_ = 0;
668 }
669
670 static const int kV8WrapperObjectIndex = 1;
671
672 private:
IsValidApiObject(Local<Object> object)673 static bool IsValidApiObject(Local<Object> object) {
674 i::Address addr = *reinterpret_cast<i::Address*>(*object);
675 auto instance_type = i::Internals::GetInstanceType(addr);
676 return (base::IsInRange(instance_type, i::Internals::kFirstJSApiObjectType,
677 i::Internals::kLastJSApiObjectType) ||
678 instance_type == i::Internals::kJSSpecialApiObjectType);
679 }
UnwrapObject(Local<Object> object)680 static FastCApiObject* UnwrapObject(Local<Object> object) {
681 if (!IsValidApiObject(object)) {
682 return nullptr;
683 }
684 FastCApiObject* wrapped = reinterpret_cast<FastCApiObject*>(
685 object->GetAlignedPointerFromInternalField(kV8WrapperObjectIndex));
686 CHECK_NOT_NULL(wrapped);
687 return wrapped;
688 }
689 int fast_call_count_ = 0, slow_call_count_ = 0;
690 #ifdef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
691 bool supports_fp_params_ = true;
692 #else // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
693 bool supports_fp_params_ = false;
694 #endif // V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
695 };
696
697 #undef CHECK_SELF_OR_THROW
698 #undef CHECK_SELF_OR_FALLBACK
699
700 // The object is statically initialized for simplicity, typically the embedder
701 // will take care of managing their C++ objects lifetime.
702 thread_local FastCApiObject kFastCApiObject;
703 } // namespace
704
705 // static
instance()706 FastCApiObject& FastCApiObject::instance() { return kFastCApiObject; }
707
CreateFastCAPIObject(const FunctionCallbackInfo<Value> & info)708 void CreateFastCAPIObject(const FunctionCallbackInfo<Value>& info) {
709 if (!info.IsConstructCall()) {
710 info.GetIsolate()->ThrowError(
711 "FastCAPI helper must be constructed with new.");
712 return;
713 }
714 Local<Object> api_object = info.Holder();
715 api_object->SetAlignedPointerInInternalField(
716 FastCApiObject::kV8WrapperObjectIndex,
717 reinterpret_cast<void*>(&kFastCApiObject));
718 api_object->SetAccessorProperty(
719 String::NewFromUtf8Literal(info.GetIsolate(), "supports_fp_params"),
720 FunctionTemplate::New(info.GetIsolate(), FastCApiObject::SupportsFPParams)
721 ->GetFunction(api_object->GetCreationContext().ToLocalChecked())
722 .ToLocalChecked());
723 }
724
CreateTestFastCApiTemplate(Isolate * isolate)725 Local<FunctionTemplate> Shell::CreateTestFastCApiTemplate(Isolate* isolate) {
726 Local<FunctionTemplate> api_obj_ctor =
727 FunctionTemplate::New(isolate, CreateFastCAPIObject);
728 PerIsolateData::Get(isolate)->SetTestApiObjectCtor(api_obj_ctor);
729 Local<Signature> signature = Signature::New(isolate, api_obj_ctor);
730 {
731 CFunction add_all_c_func =
732 CFunction::Make(FastCApiObject::AddAllFastCallback V8_IF_USE_SIMULATOR(
733 FastCApiObject::AddAllFastCallbackPatch));
734 api_obj_ctor->PrototypeTemplate()->Set(
735 isolate, "add_all",
736 FunctionTemplate::New(isolate, FastCApiObject::AddAllSlowCallback,
737 Local<Value>(), signature, 1,
738 ConstructorBehavior::kThrow,
739 SideEffectType::kHasSideEffect, &add_all_c_func));
740
741 CFunction add_all_seq_c_func = CFunction::Make(
742 FastCApiObject::AddAllSequenceFastCallback V8_IF_USE_SIMULATOR(
743 FastCApiObject::AddAllSequenceFastCallbackPatch));
744 api_obj_ctor->PrototypeTemplate()->Set(
745 isolate, "add_all_sequence",
746 FunctionTemplate::New(
747 isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(),
748 signature, 1, ConstructorBehavior::kThrow,
749 SideEffectType::kHasSideEffect, &add_all_seq_c_func));
750
751 CFunction add_all_int32_typed_array_c_func = CFunction::Make(
752 FastCApiObject::AddAllTypedArrayFastCallback<int32_t>
753 V8_IF_USE_SIMULATOR(
754 FastCApiObject::AddAllTypedArrayFastCallbackPatch<int32_t>));
755
756 api_obj_ctor->PrototypeTemplate()->Set(
757 isolate, "add_all_int32_typed_array",
758 FunctionTemplate::New(
759 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
760 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
761 SideEffectType::kHasSideEffect, &add_all_int32_typed_array_c_func));
762
763 CFunction add_all_int64_typed_array_c_func = CFunction::Make(
764 FastCApiObject::AddAllTypedArrayFastCallback<int64_t>
765 V8_IF_USE_SIMULATOR(
766 FastCApiObject::AddAllTypedArrayFastCallbackPatch<int64_t>));
767 api_obj_ctor->PrototypeTemplate()->Set(
768 isolate, "add_all_int64_typed_array",
769 FunctionTemplate::New(
770 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
771 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
772 SideEffectType::kHasSideEffect, &add_all_int64_typed_array_c_func));
773
774 CFunction add_all_uint64_typed_array_c_func = CFunction::Make(
775 FastCApiObject::AddAllTypedArrayFastCallback<uint64_t>
776 V8_IF_USE_SIMULATOR(
777 FastCApiObject::AddAllTypedArrayFastCallbackPatch<uint64_t>));
778 api_obj_ctor->PrototypeTemplate()->Set(
779 isolate, "add_all_uint64_typed_array",
780 FunctionTemplate::New(
781 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
782 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
783 SideEffectType::kHasSideEffect,
784 &add_all_uint64_typed_array_c_func));
785
786 CFunction add_all_uint32_typed_array_c_func = CFunction::Make(
787 FastCApiObject::AddAllTypedArrayFastCallback<uint32_t>
788 V8_IF_USE_SIMULATOR(
789 FastCApiObject::AddAllTypedArrayFastCallbackPatch<uint32_t>));
790 api_obj_ctor->PrototypeTemplate()->Set(
791 isolate, "add_all_uint32_typed_array",
792 FunctionTemplate::New(
793 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
794 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
795 SideEffectType::kHasSideEffect,
796 &add_all_uint32_typed_array_c_func));
797
798 CFunction add_all_float32_typed_array_c_func = CFunction::Make(
799 FastCApiObject::AddAllTypedArrayFastCallback<float> V8_IF_USE_SIMULATOR(
800 FastCApiObject::AddAllTypedArrayFastCallbackPatch<float>));
801 api_obj_ctor->PrototypeTemplate()->Set(
802 isolate, "add_all_float32_typed_array",
803 FunctionTemplate::New(
804 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
805 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
806 SideEffectType::kHasSideEffect,
807 &add_all_float32_typed_array_c_func));
808
809 CFunction add_all_float64_typed_array_c_func = CFunction::Make(
810 FastCApiObject::AddAllTypedArrayFastCallback<double>
811 V8_IF_USE_SIMULATOR(
812 FastCApiObject::AddAllTypedArrayFastCallbackPatch<double>));
813 api_obj_ctor->PrototypeTemplate()->Set(
814 isolate, "add_all_float64_typed_array",
815 FunctionTemplate::New(
816 isolate, FastCApiObject::AddAllTypedArraySlowCallback,
817 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
818 SideEffectType::kHasSideEffect,
819 &add_all_float64_typed_array_c_func));
820
821 const CFunction add_all_overloads[] = {
822 add_all_uint32_typed_array_c_func,
823 add_all_seq_c_func,
824 };
825 api_obj_ctor->PrototypeTemplate()->Set(
826 isolate, "add_all_overload",
827 FunctionTemplate::NewWithCFunctionOverloads(
828 isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(),
829 signature, 1, ConstructorBehavior::kThrow,
830 SideEffectType::kHasSideEffect, {add_all_overloads, 2}));
831
832 CFunction add_all_int_invalid_func =
833 CFunction::Make(FastCApiObject::AddAllIntInvalidCallback);
834 const CFunction add_all_invalid_overloads[] = {
835 add_all_int_invalid_func,
836 add_all_seq_c_func,
837 };
838 api_obj_ctor->PrototypeTemplate()->Set(
839 isolate, "add_all_invalid_overload",
840 FunctionTemplate::NewWithCFunctionOverloads(
841 isolate, FastCApiObject::AddAllSequenceSlowCallback, Local<Value>(),
842 signature, 1, ConstructorBehavior::kThrow,
843 SideEffectType::kHasSideEffect, {add_all_invalid_overloads, 2}));
844
845 CFunction add_all_32bit_int_8args_c_func = CFunction::Make(
846 FastCApiObject::AddAll32BitIntFastCallback_8Args V8_IF_USE_SIMULATOR(
847 FastCApiObject::AddAll32BitIntFastCallback_8ArgsPatch));
848 CFunction add_all_32bit_int_6args_c_func = CFunction::Make(
849 FastCApiObject::AddAll32BitIntFastCallback_6Args V8_IF_USE_SIMULATOR(
850 FastCApiObject::AddAll32BitIntFastCallback_6ArgsPatch));
851 CFunction add_all_32bit_int_5args_c_func = CFunction::Make(
852 FastCApiObject::AddAll32BitIntFastCallback_5Args V8_IF_USE_SIMULATOR(
853 FastCApiObject::AddAll32BitIntFastCallback_5ArgsPatch));
854 const CFunction c_function_overloads[] = {add_all_32bit_int_6args_c_func,
855 add_all_32bit_int_5args_c_func};
856
857 api_obj_ctor->PrototypeTemplate()->Set(
858 isolate, "overloaded_add_all_32bit_int",
859 FunctionTemplate::NewWithCFunctionOverloads(
860 isolate, FastCApiObject::AddAll32BitIntSlowCallback, Local<Value>(),
861 signature, 1, ConstructorBehavior::kThrow,
862 SideEffectType::kHasSideEffect, {c_function_overloads, 2}));
863
864 api_obj_ctor->PrototypeTemplate()->Set(
865 isolate, "overloaded_add_all_8args",
866 FunctionTemplate::New(
867 isolate, FastCApiObject::AddAll32BitIntSlowCallback, Local<Value>(),
868 signature, 1, ConstructorBehavior::kThrow,
869 SideEffectType::kHasSideEffect, &add_all_32bit_int_8args_c_func));
870
871 api_obj_ctor->PrototypeTemplate()->Set(
872 isolate, "overloaded_add_all_32bit_int_no_sig",
873 FunctionTemplate::NewWithCFunctionOverloads(
874 isolate, FastCApiObject::AddAll32BitIntSlowCallback, Local<Value>(),
875 Local<Signature>(), 1, ConstructorBehavior::kThrow,
876 SideEffectType::kHasSideEffect, {c_function_overloads, 2}));
877
878 CFunction add_all_no_options_c_func = CFunction::Make(
879 FastCApiObject::AddAllFastCallbackNoOptions V8_IF_USE_SIMULATOR(
880 FastCApiObject::AddAllFastCallbackNoOptionsPatch));
881 api_obj_ctor->PrototypeTemplate()->Set(
882 isolate, "add_all_no_options",
883 FunctionTemplate::New(
884 isolate, FastCApiObject::AddAllSlowCallback, Local<Value>(),
885 Local<Signature>(), 1, ConstructorBehavior::kThrow,
886 SideEffectType::kHasSideEffect, &add_all_no_options_c_func));
887
888 CFunction add_32bit_int_c_func = CFunction::Make(
889 FastCApiObject::Add32BitIntFastCallback V8_IF_USE_SIMULATOR(
890 FastCApiObject::Add32BitIntFastCallbackPatch));
891 api_obj_ctor->PrototypeTemplate()->Set(
892 isolate, "add_32bit_int",
893 FunctionTemplate::New(
894 isolate, FastCApiObject::Add32BitIntSlowCallback, Local<Value>(),
895 signature, 1, ConstructorBehavior::kThrow,
896 SideEffectType::kHasSideEffect, &add_32bit_int_c_func));
897
898 CFunction is_valid_api_object_c_func =
899 CFunction::Make(FastCApiObject::IsFastCApiObjectFastCallback);
900 api_obj_ctor->PrototypeTemplate()->Set(
901 isolate, "is_fast_c_api_object",
902 FunctionTemplate::New(
903 isolate, FastCApiObject::IsFastCApiObjectSlowCallback,
904 Local<Value>(), signature, 1, ConstructorBehavior::kThrow,
905 SideEffectType::kHasSideEffect, &is_valid_api_object_c_func));
906
907 api_obj_ctor->PrototypeTemplate()->Set(
908 isolate, "fast_call_count",
909 FunctionTemplate::New(
910 isolate, FastCApiObject::FastCallCount, Local<Value>(), signature,
911 1, ConstructorBehavior::kThrow, SideEffectType::kHasNoSideEffect));
912 api_obj_ctor->PrototypeTemplate()->Set(
913 isolate, "slow_call_count",
914 FunctionTemplate::New(
915 isolate, FastCApiObject::SlowCallCount, Local<Value>(), signature,
916 1, ConstructorBehavior::kThrow, SideEffectType::kHasNoSideEffect));
917 api_obj_ctor->PrototypeTemplate()->Set(
918 isolate, "reset_counts",
919 FunctionTemplate::New(isolate, FastCApiObject::ResetCounts,
920 Local<Value>(), signature, 1,
921 ConstructorBehavior::kThrow));
922 }
923 api_obj_ctor->InstanceTemplate()->SetInternalFieldCount(
924 FastCApiObject::kV8WrapperObjectIndex + 1);
925
926 return api_obj_ctor;
927 }
928
CreateLeafInterfaceObject(const FunctionCallbackInfo<Value> & info)929 void CreateLeafInterfaceObject(const FunctionCallbackInfo<Value>& info) {
930 if (!info.IsConstructCall()) {
931 info.GetIsolate()->ThrowError(
932 "LeafInterfaceType helper must be constructed with new.");
933 }
934 }
935
CreateLeafInterfaceTypeTemplate(Isolate * isolate)936 Local<FunctionTemplate> Shell::CreateLeafInterfaceTypeTemplate(
937 Isolate* isolate) {
938 Local<FunctionTemplate> leaf_object_ctor =
939 FunctionTemplate::New(isolate, CreateLeafInterfaceObject);
940 leaf_object_ctor->SetClassName(
941 String::NewFromUtf8Literal(isolate, "LeafInterfaceType"));
942 return leaf_object_ctor;
943 }
944
945 } // namespace v8
946