koffi 1.1.5 → 1.2.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,17 +46,67 @@ struct Xmm0Xmm1Ret {
46
46
  double xmm1;
47
47
  };
48
48
 
49
- extern "C" RaxRdxRet ForwardCallGG(const void *func, uint8_t *sp);
50
- extern "C" float ForwardCallF(const void *func, uint8_t *sp);
51
- extern "C" Xmm0RaxRet ForwardCallDG(const void *func, uint8_t *sp);
52
- extern "C" RaxXmm0Ret ForwardCallGD(const void *func, uint8_t *sp);
53
- extern "C" Xmm0Xmm1Ret ForwardCallDD(const void *func, uint8_t *sp);
54
-
55
- extern "C" RaxRdxRet ForwardCallXGG(const void *func, uint8_t *sp);
56
- extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
57
- extern "C" Xmm0RaxRet ForwardCallXDG(const void *func, uint8_t *sp);
58
- extern "C" RaxXmm0Ret ForwardCallXGD(const void *func, uint8_t *sp);
59
- extern "C" Xmm0Xmm1Ret ForwardCallXDD(const void *func, uint8_t *sp);
49
+ struct BackRegisters {
50
+ uint64_t rax;
51
+ uint64_t rdx;
52
+ double xmm0;
53
+ double xmm1;
54
+ };
55
+
56
+ extern "C" RaxRdxRet ForwardCallGG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
57
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
58
+ extern "C" Xmm0RaxRet ForwardCallDG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
59
+ extern "C" RaxXmm0Ret ForwardCallGD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
60
+ extern "C" Xmm0Xmm1Ret ForwardCallDD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
61
+
62
+ extern "C" RaxRdxRet ForwardCallXGG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
63
+ extern "C" float ForwardCallXF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
64
+ extern "C" Xmm0RaxRet ForwardCallXDG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
65
+ extern "C" RaxXmm0Ret ForwardCallXGD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
66
+ extern "C" Xmm0Xmm1Ret ForwardCallXDD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
67
+
68
+ extern "C" int Trampoline0; extern "C" int TrampolineX0;
69
+ extern "C" int Trampoline1; extern "C" int TrampolineX1;
70
+ extern "C" int Trampoline2; extern "C" int TrampolineX2;
71
+ extern "C" int Trampoline3; extern "C" int TrampolineX3;
72
+ extern "C" int Trampoline4; extern "C" int TrampolineX4;
73
+ extern "C" int Trampoline5; extern "C" int TrampolineX5;
74
+ extern "C" int Trampoline6; extern "C" int TrampolineX6;
75
+ extern "C" int Trampoline7; extern "C" int TrampolineX7;
76
+ extern "C" int Trampoline8; extern "C" int TrampolineX8;
77
+ extern "C" int Trampoline9; extern "C" int TrampolineX9;
78
+ extern "C" int Trampoline10; extern "C" int TrampolineX10;
79
+ extern "C" int Trampoline11; extern "C" int TrampolineX11;
80
+ extern "C" int Trampoline12; extern "C" int TrampolineX12;
81
+ extern "C" int Trampoline13; extern "C" int TrampolineX13;
82
+ extern "C" int Trampoline14; extern "C" int TrampolineX14;
83
+ extern "C" int Trampoline15; extern "C" int TrampolineX15;
84
+
85
+ extern "C" napi_value CallSwitchStack(Napi::Function *func, size_t argc, napi_value *argv,
86
+ uint8_t *old_sp, Span<uint8_t> *new_stack,
87
+ napi_value (*call)(Napi::Function *func, size_t argc, napi_value *argv));
88
+
89
+ static void *const Trampolines[][2] = {
90
+ { &Trampoline0, &TrampolineX0 },
91
+ { &Trampoline1, &TrampolineX1 },
92
+ { &Trampoline2, &TrampolineX2 },
93
+ { &Trampoline3, &TrampolineX3 },
94
+ { &Trampoline4, &TrampolineX4 },
95
+ { &Trampoline5, &TrampolineX5 },
96
+ { &Trampoline6, &TrampolineX6 },
97
+ { &Trampoline7, &TrampolineX7 },
98
+ { &Trampoline8, &TrampolineX8 },
99
+ { &Trampoline9, &TrampolineX9 },
100
+ { &Trampoline10, &TrampolineX10 },
101
+ { &Trampoline11, &TrampolineX11 },
102
+ { &Trampoline12, &TrampolineX12 },
103
+ { &Trampoline13, &TrampolineX13 },
104
+ { &Trampoline14, &TrampolineX14 },
105
+ { &Trampoline15, &TrampolineX15 }
106
+ };
107
+ RG_STATIC_ASSERT(RG_LEN(Trampolines) == MaxTrampolines);
108
+
109
+ static RG_THREAD_LOCAL CallData *exec_call;
60
110
 
61
111
  static inline RegisterClass MergeClasses(RegisterClass cls1, RegisterClass cls2)
62
112
  {
@@ -94,7 +144,8 @@ static Size ClassifyType(const TypeInfo *type, Size offset, Span<RegisterClass>
94
144
  case PrimitiveKind::UInt64:
95
145
  case PrimitiveKind::String:
96
146
  case PrimitiveKind::String16:
97
- case PrimitiveKind::Pointer: {
147
+ case PrimitiveKind::Pointer:
148
+ case PrimitiveKind::Callback: {
98
149
  classes[0] = MergeClasses(classes[0], RegisterClass::Integer);
99
150
  return 1;
100
151
  } break;
@@ -197,7 +248,7 @@ bool AnalyseFunction(InstanceData *, FunctionInfo *func)
197
248
 
198
249
  bool CallData::Prepare(const Napi::CallbackInfo &info)
199
250
  {
200
- uint8_t *args_ptr = nullptr;
251
+ uint64_t *args_ptr = nullptr;
201
252
  uint64_t *gpr_ptr = nullptr;
202
253
  uint64_t *xmm_ptr = nullptr;
203
254
 
@@ -231,13 +282,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
231
282
  }
232
283
 
233
284
  bool b = value.As<Napi::Boolean>();
234
-
235
- if (RG_LIKELY(param.gpr_count)) {
236
- *(gpr_ptr++) = (uint64_t)b;
237
- } else {
238
- *args_ptr = (uint8_t)b;
239
- args_ptr += 8;
240
- }
285
+ *((param.gpr_count ? gpr_ptr : args_ptr)++) = (uint64_t)b;
241
286
  } break;
242
287
  case PrimitiveKind::Int8:
243
288
  case PrimitiveKind::UInt8:
@@ -245,22 +290,23 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
245
290
  case PrimitiveKind::UInt16:
246
291
  case PrimitiveKind::Int32:
247
292
  case PrimitiveKind::UInt32:
248
- case PrimitiveKind::Int64:
249
- case PrimitiveKind::UInt64: {
293
+ case PrimitiveKind::Int64: {
250
294
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
251
295
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
252
296
  return false;
253
297
  }
254
298
 
255
299
  int64_t v = CopyNumber<int64_t>(value);
256
-
257
- if (RG_LIKELY(param.gpr_count)) {
258
- *(gpr_ptr++) = (uint64_t)v;
259
- } else {
260
- args_ptr = AlignUp(args_ptr, param.type->align);
261
- memcpy(args_ptr, &v, param.type->size); // Little Endian
262
- args_ptr += 8;
300
+ *(int64_t *)((param.gpr_count ? gpr_ptr : args_ptr)++) = v;
301
+ } break;
302
+ case PrimitiveKind::UInt64: {
303
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
304
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
305
+ return false;
263
306
  }
307
+
308
+ uint64_t v = CopyNumber<uint64_t>(value);
309
+ *((param.gpr_count ? gpr_ptr : args_ptr)++) = v;
264
310
  } break;
265
311
  case PrimitiveKind::String: {
266
312
  const char *str;
@@ -275,13 +321,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
275
321
  return false;
276
322
  }
277
323
 
278
- if (RG_LIKELY(param.gpr_count)) {
279
- *(gpr_ptr++) = (uint64_t)str;
280
- } else {
281
- args_ptr = AlignUp(args_ptr, 8);
282
- *(uint64_t *)args_ptr = (uint64_t)str;
283
- args_ptr += 8;
284
- }
324
+ *(const char **)((param.gpr_count ? gpr_ptr : args_ptr)++) = str;
285
325
  } break;
286
326
  case PrimitiveKind::String16: {
287
327
  const char16_t *str16;
@@ -296,13 +336,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
296
336
  return false;
297
337
  }
298
338
 
299
- if (RG_LIKELY(param.gpr_count)) {
300
- *(gpr_ptr++) = (uint64_t)str16;
301
- } else {
302
- args_ptr = AlignUp(args_ptr, 8);
303
- *(uint64_t *)args_ptr = (uint64_t)str16;
304
- args_ptr += 8;
305
- }
339
+ *(const char16_t **)((param.gpr_count ? gpr_ptr : args_ptr)++) = str16;
306
340
  } break;
307
341
  case PrimitiveKind::Pointer: {
308
342
  uint8_t *ptr;
@@ -335,13 +369,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
335
369
  return false;
336
370
  }
337
371
 
338
- if (RG_LIKELY(param.gpr_count)) {
339
- *(gpr_ptr++) = (uint64_t)ptr;
340
- } else {
341
- args_ptr = AlignUp(args_ptr, 8);
342
- *(uint64_t *)args_ptr = (uint64_t)ptr;
343
- args_ptr += 8;
344
- }
372
+ *(void **)((param.gpr_count ? gpr_ptr : args_ptr)++) = ptr;
345
373
  } break;
346
374
  case PrimitiveKind::Record: {
347
375
  if (RG_UNLIKELY(!IsObject(value))) {
@@ -357,10 +385,9 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
357
385
  uint64_t buf[2] = {};
358
386
  if (!PushObject(obj, param.type, (uint8_t *)buf))
359
387
  return false;
388
+ uint64_t *ptr = buf;
360
389
 
361
390
  if (param.gpr_first) {
362
- uint64_t *ptr = buf;
363
-
364
391
  *(gpr_ptr++) = *(ptr++);
365
392
  if (param.gpr_count == 2) {
366
393
  *(gpr_ptr++) = *(ptr++);
@@ -368,8 +395,6 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
368
395
  *(xmm_ptr++) = *(ptr++);
369
396
  }
370
397
  } else {
371
- uint64_t *ptr = buf;
372
-
373
398
  *(xmm_ptr++) = *(ptr++);
374
399
  if (param.xmm_count == 2) {
375
400
  *(xmm_ptr++) = *(ptr++);
@@ -379,9 +404,9 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
379
404
  }
380
405
  } else if (param.use_memory) {
381
406
  args_ptr = AlignUp(args_ptr, param.type->align);
382
- if (!PushObject(obj, param.type, args_ptr))
407
+ if (!PushObject(obj, param.type, (uint8_t *)args_ptr))
383
408
  return false;
384
- args_ptr += AlignLen(param.type->size, 8);
409
+ args_ptr += (param.type->size + 7) / 8;
385
410
  }
386
411
  } break;
387
412
  case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
@@ -392,14 +417,7 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
392
417
  }
393
418
 
394
419
  float f = CopyNumber<float>(value);
395
-
396
- if (RG_LIKELY(param.xmm_count)) {
397
- memcpy(xmm_ptr++, &f, 4);
398
- } else {
399
- args_ptr = AlignUp(args_ptr, 4);
400
- memcpy(args_ptr, &f, 4);
401
- args_ptr += 8;
402
- }
420
+ *(float *)((param.xmm_count ? xmm_ptr : args_ptr)++) = f;
403
421
  } break;
404
422
  case PrimitiveKind::Float64: {
405
423
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
@@ -408,29 +426,46 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
408
426
  }
409
427
 
410
428
  double d = CopyNumber<double>(value);
429
+ *(double *)((param.xmm_count ? xmm_ptr : args_ptr)++) = d;
430
+ } break;
431
+ case PrimitiveKind::Callback: {
432
+ void *ptr;
433
+
434
+ if (value.IsFunction()) {
435
+ Napi::Function func = value.As<Napi::Function>();
411
436
 
412
- if (RG_LIKELY(param.xmm_count)) {
413
- memcpy(xmm_ptr++, &d, 8);
437
+ Size idx = ReserveTrampoline(param.type->proto, func);
438
+ if (RG_UNLIKELY(idx < 0))
439
+ return false;
440
+
441
+ ptr = GetTrampoline(idx, param.type->proto);
442
+ } else if (CheckValueTag(instance, value, param.type)) {
443
+ ptr = value.As<Napi::External<void>>().Data();
444
+ } else if (IsNullOrUndefined(value)) {
445
+ ptr = nullptr;
414
446
  } else {
415
- args_ptr = AlignUp(args_ptr, 8);
416
- memcpy(args_ptr, &d, 8);
417
- args_ptr += 8;
447
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
448
+ return false;
418
449
  }
450
+
451
+ *(void **)((param.gpr_count ? gpr_ptr : args_ptr)++) = ptr;
419
452
  } break;
420
453
  }
421
454
  }
422
455
 
423
- sp = mem->stack.end();
456
+ new_sp = mem->stack.end();
424
457
 
425
458
  return true;
426
459
  }
427
460
 
428
461
  void CallData::Execute()
429
462
  {
463
+ exec_call = this;
464
+
430
465
  #define PERFORM_CALL(Suffix) \
431
466
  ([&]() { \
432
- auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, sp) \
433
- : ForwardCall ## Suffix(func->func, sp)); \
467
+ auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, new_sp, &old_sp) \
468
+ : ForwardCall ## Suffix(func->func, new_sp, &old_sp)); \
434
469
  return ret; \
435
470
  })()
436
471
 
@@ -448,7 +483,8 @@ void CallData::Execute()
448
483
  case PrimitiveKind::UInt64:
449
484
  case PrimitiveKind::String:
450
485
  case PrimitiveKind::String16:
451
- case PrimitiveKind::Pointer: { result.u64 = PERFORM_CALL(GG).rax; } break;
486
+ case PrimitiveKind::Pointer:
487
+ case PrimitiveKind::Callback: { result.u64 = PERFORM_CALL(GG).rax; } break;
452
488
  case PrimitiveKind::Record: {
453
489
  if (func->ret.gpr_first && !func->ret.xmm_count) {
454
490
  RaxRdxRet ret = PERFORM_CALL(GG);
@@ -492,11 +528,16 @@ Napi::Value CallData::Complete()
492
528
  case PrimitiveKind::UInt64: return Napi::BigInt::New(env, result.u64);
493
529
  case PrimitiveKind::String: return Napi::String::New(env, (const char *)result.ptr);
494
530
  case PrimitiveKind::String16: return Napi::String::New(env, (const char16_t *)result.ptr);
495
- case PrimitiveKind::Pointer: {
496
- Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
497
- SetValueTag(instance, external, func->ret.type);
531
+ case PrimitiveKind::Pointer:
532
+ case PrimitiveKind::Callback: {
533
+ if (result.ptr) {
534
+ Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
535
+ SetValueTag(instance, external, func->ret.type);
498
536
 
499
- return external;
537
+ return external;
538
+ } else {
539
+ return env.Null();
540
+ }
500
541
  } break;
501
542
  case PrimitiveKind::Record: {
502
543
  const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
@@ -513,6 +554,337 @@ Napi::Value CallData::Complete()
513
554
  RG_UNREACHABLE();
514
555
  }
515
556
 
557
+ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
558
+ {
559
+ const FunctionInfo *proto = instance->trampolines[idx].proto;
560
+ Napi::Function func = instance->trampolines[idx].func;
561
+
562
+ // Allow reuse of static trampoline
563
+ instance->free_trampolines |= 1u << idx;
564
+ used_trampolines &= ~(1u << idx);
565
+
566
+ uint64_t *gpr_ptr = (uint64_t *)own_sp;
567
+ uint64_t *xmm_ptr = gpr_ptr + 6;
568
+ uint64_t *args_ptr = (uint64_t *)caller_sp;
569
+
570
+ uint8_t *return_ptr = proto->ret.use_memory ? (uint8_t *)gpr_ptr[0] : nullptr;
571
+ gpr_ptr += proto->ret.use_memory;
572
+
573
+ LocalArray<napi_value, MaxParameters> arguments;
574
+
575
+ // Convert to JS arguments
576
+ for (Size i = 0; i < proto->parameters.len; i++) {
577
+ const ParameterInfo &param = proto->parameters[i];
578
+ RG_ASSERT(param.directions >= 1 && param.directions <= 3);
579
+
580
+ switch (param.type->primitive) {
581
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
582
+
583
+ case PrimitiveKind::Bool: {
584
+ bool b = *(bool *)((param.gpr_count ? gpr_ptr : args_ptr)++);
585
+
586
+ Napi::Value arg = Napi::Boolean::New(env, b);
587
+ arguments.Append(arg);
588
+ } break;
589
+ case PrimitiveKind::Int8: {
590
+ double d = (double)*(int8_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
591
+
592
+ Napi::Value arg = Napi::Number::New(env, d);
593
+ arguments.Append(arg);
594
+ } break;
595
+ case PrimitiveKind::UInt8: {
596
+ double d = (double)*(uint8_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
597
+
598
+ Napi::Value arg = Napi::Number::New(env, d);
599
+ arguments.Append(arg);
600
+ } break;
601
+ case PrimitiveKind::Int16: {
602
+ double d = (double)*(int16_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
603
+
604
+ Napi::Value arg = Napi::Number::New(env, d);
605
+ arguments.Append(arg);
606
+ } break;
607
+ case PrimitiveKind::UInt16: {
608
+ double d = (double)*(uint16_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
609
+
610
+ Napi::Value arg = Napi::Number::New(env, d);
611
+ arguments.Append(arg);
612
+ } break;
613
+ case PrimitiveKind::Int32: {
614
+ double d = (double)*(int32_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
615
+
616
+ Napi::Value arg = Napi::Number::New(env, d);
617
+ arguments.Append(arg);
618
+ } break;
619
+ case PrimitiveKind::UInt32: {
620
+ double d = (double)*(int32_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
621
+
622
+ Napi::Value arg = Napi::Number::New(env, d);
623
+ arguments.Append(arg);
624
+ } break;
625
+ case PrimitiveKind::Int64: {
626
+ int64_t v = *(int64_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
627
+
628
+ Napi::Value arg = Napi::BigInt::New(env, v);
629
+ arguments.Append(arg);
630
+ } break;
631
+ case PrimitiveKind::UInt64: {
632
+ uint64_t v = *(uint64_t *)((param.gpr_count ? gpr_ptr : args_ptr)++);
633
+
634
+ Napi::Value arg = Napi::BigInt::New(env, v);
635
+ arguments.Append(arg);
636
+ } break;
637
+ case PrimitiveKind::String: {
638
+ const char *str = *(const char **)((param.gpr_count ? gpr_ptr : args_ptr)++);
639
+
640
+ Napi::Value arg = Napi::String::New(env, str);
641
+ arguments.Append(arg);
642
+ } break;
643
+ case PrimitiveKind::String16: {
644
+ const char16_t *str16 = *(const char16_t **)((param.gpr_count ? gpr_ptr : args_ptr)++);
645
+
646
+ Napi::Value arg = Napi::String::New(env, str16);
647
+ arguments.Append(arg);
648
+ } break;
649
+ case PrimitiveKind::Pointer:
650
+ case PrimitiveKind::Callback: {
651
+ void *ptr2 = *(void **)((param.gpr_count ? gpr_ptr : args_ptr)++);
652
+
653
+ if (ptr2) {
654
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
655
+ SetValueTag(instance, external, param.type);
656
+
657
+ arguments.Append(external);
658
+ } else {
659
+ arguments.Append(env.Null());
660
+ }
661
+ } break;
662
+ case PrimitiveKind::Record: {
663
+ if (param.gpr_count || param.xmm_count) {
664
+ RG_ASSERT(param.type->size <= 16);
665
+
666
+ uint64_t buf[2] = {};
667
+ uint64_t *ptr = buf;
668
+
669
+ if (param.gpr_first) {
670
+ *(ptr++) = *(gpr_ptr++);
671
+ if (param.gpr_count == 2) {
672
+ *(ptr++) = *(gpr_ptr++);
673
+ } else if (param.xmm_count == 1) {
674
+ *(ptr++) = *(xmm_ptr++);
675
+ }
676
+ } else {
677
+ *(ptr++) = *(xmm_ptr++);
678
+ if (param.xmm_count == 2) {
679
+ *(ptr++) = *(xmm_ptr++);
680
+ } else if (param.gpr_count == 1) {
681
+ *(ptr++) = *(gpr_ptr++);
682
+ }
683
+ }
684
+
685
+ Napi::Object obj = PopObject((const uint8_t *)buf, param.type);
686
+ arguments.Append(obj);
687
+ } else if (param.use_memory) {
688
+ args_ptr = AlignUp(args_ptr, param.type->align);
689
+
690
+ Napi::Object obj = PopObject((const uint8_t *)args_ptr, param.type);
691
+ arguments.Append(obj);
692
+
693
+ args_ptr += (param.type->size + 7) / 8;
694
+ }
695
+ } break;
696
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
697
+ case PrimitiveKind::Float32: {
698
+ float f = *(float *)((param.xmm_count ? xmm_ptr : args_ptr)++);
699
+
700
+ Napi::Value arg = Napi::Number::New(env, (double)f);
701
+ arguments.Append(arg);
702
+ } break;
703
+ case PrimitiveKind::Float64: {
704
+ double d = *(double *)((param.xmm_count ? xmm_ptr : args_ptr)++);
705
+
706
+ Napi::Value arg = Napi::Number::New(env, d);
707
+ arguments.Append(arg);
708
+ } break;
709
+ }
710
+ }
711
+
712
+ const TypeInfo *type = proto->ret.type;
713
+
714
+ // Make the call
715
+ napi_value ret = CallSwitchStack(&func, (size_t)arguments.len, arguments.data, old_sp, &mem->stack,
716
+ [](Napi::Function *func, size_t argc, napi_value *argv) { return (napi_value)func->Call(argc, argv); });
717
+ Napi::Value value(env, ret);
718
+
719
+ // Convert the result
720
+ switch (type->primitive) {
721
+ case PrimitiveKind::Void: {} break;
722
+ case PrimitiveKind::Bool: {
723
+ if (RG_UNLIKELY(!value.IsBoolean())) {
724
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected boolean", GetValueType(instance, value));
725
+ return;
726
+ }
727
+
728
+ bool b = value.As<Napi::Boolean>();
729
+ out_reg->rax = (uint64_t)b;
730
+ } break;
731
+ case PrimitiveKind::Int8:
732
+ case PrimitiveKind::UInt8:
733
+ case PrimitiveKind::Int16:
734
+ case PrimitiveKind::UInt16:
735
+ case PrimitiveKind::Int32:
736
+ case PrimitiveKind::UInt32:
737
+ case PrimitiveKind::Int64:
738
+ case PrimitiveKind::UInt64: {
739
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
740
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
741
+ return;
742
+ }
743
+
744
+ int64_t v = CopyNumber<int64_t>(value);
745
+ out_reg->rax = (uint64_t)v;
746
+ } break;
747
+ case PrimitiveKind::String: {
748
+ const char *str;
749
+ if (RG_LIKELY(value.IsString())) {
750
+ str = PushString(value);
751
+ if (RG_UNLIKELY(!str))
752
+ return;
753
+ } else if (IsNullOrUndefined(value)) {
754
+ str = nullptr;
755
+ } else {
756
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
757
+ return;
758
+ }
759
+
760
+ out_reg->rax = (uint64_t)str;
761
+ } break;
762
+ case PrimitiveKind::String16: {
763
+ const char16_t *str16;
764
+ if (RG_LIKELY(value.IsString())) {
765
+ str16 = PushString16(value);
766
+ if (RG_UNLIKELY(!str16))
767
+ return;
768
+ } else if (IsNullOrUndefined(value)) {
769
+ str16 = nullptr;
770
+ } else {
771
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
772
+ return;
773
+ }
774
+
775
+ out_reg->rax = (uint64_t)str16;
776
+ } break;
777
+ case PrimitiveKind::Pointer: {
778
+ uint8_t *ptr;
779
+
780
+ if (CheckValueTag(instance, value, type)) {
781
+ ptr = value.As<Napi::External<uint8_t>>().Data();
782
+ } else if (IsObject(value) && type->ref->primitive == PrimitiveKind::Record) {
783
+ Napi::Object obj = value.As<Napi::Object>();
784
+
785
+ if (RG_UNLIKELY(!AllocHeap(type->ref->size, 16, &ptr)))
786
+ return;
787
+
788
+ if (!PushObject(obj, type->ref, ptr))
789
+ return;
790
+ } else if (IsNullOrUndefined(value)) {
791
+ ptr = nullptr;
792
+ } else {
793
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected %2", GetValueType(instance, value), type->name);
794
+ return;
795
+ }
796
+
797
+ out_reg->rax = (uint64_t)ptr;
798
+ } break;
799
+ case PrimitiveKind::Record: {
800
+ if (RG_UNLIKELY(!IsObject(value))) {
801
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected object", GetValueType(instance, value));
802
+ return;
803
+ }
804
+
805
+ Napi::Object obj = value.As<Napi::Object>();
806
+
807
+ if (return_ptr) {
808
+ if (!PushObject(obj, type, return_ptr))
809
+ return;
810
+ out_reg->rax = (uint64_t)return_ptr;
811
+ } else {
812
+ RG_ASSERT(type->size <= 16);
813
+
814
+ uint8_t buf[16] = {};
815
+ if (!PushObject(obj, type, buf))
816
+ return;
817
+
818
+ if (proto->ret.gpr_first && !proto->ret.xmm_count) {
819
+ memcpy(&out_reg->rax, buf + 0, 8);
820
+ memcpy(&out_reg->rdx, buf + 8, 8);
821
+ } else if (proto->ret.gpr_first) {
822
+ memcpy(&out_reg->rax, buf + 0, 8);
823
+ memcpy(&out_reg->xmm0, buf + 8, 8);
824
+ } else if (proto->ret.xmm_count == 2) {
825
+ memcpy(&out_reg->xmm0, buf + 0, 8);
826
+ memcpy(&out_reg->xmm1, buf + 8, 8);
827
+ } else {
828
+ memcpy(&out_reg->xmm0, buf + 0, 8);
829
+ memcpy(&out_reg->rax, buf + 8, 8);
830
+ }
831
+ }
832
+ } break;
833
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
834
+ case PrimitiveKind::Float32: {
835
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
836
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
837
+ return;
838
+ }
839
+
840
+ float f = CopyNumber<float>(value);
841
+ memcpy(&out_reg->xmm0, &f, 4);
842
+ } break;
843
+ case PrimitiveKind::Float64: {
844
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
845
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
846
+ return;
847
+ }
848
+
849
+ double d = CopyNumber<double>(value);
850
+ out_reg->xmm0 = d;
851
+ } break;
852
+ case PrimitiveKind::Callback: {
853
+ void *ptr;
854
+
855
+ if (value.IsFunction()) {
856
+ Napi::Function func = value.As<Napi::Function>();
857
+
858
+ Size idx = ReserveTrampoline(type->proto, func);
859
+ if (RG_UNLIKELY(idx < 0))
860
+ return;
861
+
862
+ ptr = GetTrampoline(idx, type->proto);
863
+ } else if (CheckValueTag(instance, value, type)) {
864
+ ptr = value.As<Napi::External<uint8_t>>().Data();
865
+ } else if (IsNullOrUndefined(value)) {
866
+ ptr = nullptr;
867
+ } else {
868
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected %2", GetValueType(instance, value), type->name);
869
+ return;
870
+ }
871
+
872
+ out_reg->rax = (uint64_t)ptr;
873
+ } break;
874
+ }
875
+ }
876
+
877
+ void *GetTrampoline(Size idx, const FunctionInfo *proto)
878
+ {
879
+ bool xmm = proto->forward_fp || IsFloat(proto->ret.type);
880
+ return Trampolines[idx][xmm];
881
+ }
882
+
883
+ extern "C" void RelayCallBack(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
884
+ {
885
+ exec_call->Relay(idx, own_sp, caller_sp, out_reg);
886
+ }
887
+
516
888
  }
517
889
 
518
890
  #endif