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.
@@ -22,12 +22,60 @@
22
22
 
23
23
  namespace RG {
24
24
 
25
- extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp);
26
- extern "C" float ForwardCallF(const void *func, uint8_t *sp);
27
- extern "C" double ForwardCallD(const void *func, uint8_t *sp);
28
- extern "C" uint64_t ForwardCallXG(const void *func, uint8_t *sp);
29
- extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
30
- extern "C" double ForwardCallXD(const void *func, uint8_t *sp);
25
+ struct BackRegisters {
26
+ uint64_t rax;
27
+ double xmm0;
28
+ };
29
+
30
+ extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
31
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
32
+ extern "C" double ForwardCallD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
33
+ extern "C" uint64_t ForwardCallXG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
34
+ extern "C" float ForwardCallXF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
35
+ extern "C" double ForwardCallXD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
36
+
37
+ extern "C" int Trampoline0; extern "C" int TrampolineX0;
38
+ extern "C" int Trampoline1; extern "C" int TrampolineX1;
39
+ extern "C" int Trampoline2; extern "C" int TrampolineX2;
40
+ extern "C" int Trampoline3; extern "C" int TrampolineX3;
41
+ extern "C" int Trampoline4; extern "C" int TrampolineX4;
42
+ extern "C" int Trampoline5; extern "C" int TrampolineX5;
43
+ extern "C" int Trampoline6; extern "C" int TrampolineX6;
44
+ extern "C" int Trampoline7; extern "C" int TrampolineX7;
45
+ extern "C" int Trampoline8; extern "C" int TrampolineX8;
46
+ extern "C" int Trampoline9; extern "C" int TrampolineX9;
47
+ extern "C" int Trampoline10; extern "C" int TrampolineX10;
48
+ extern "C" int Trampoline11; extern "C" int TrampolineX11;
49
+ extern "C" int Trampoline12; extern "C" int TrampolineX12;
50
+ extern "C" int Trampoline13; extern "C" int TrampolineX13;
51
+ extern "C" int Trampoline14; extern "C" int TrampolineX14;
52
+ extern "C" int Trampoline15; extern "C" int TrampolineX15;
53
+
54
+ extern "C" napi_value CallSwitchStack(Napi::Function *func, size_t argc, napi_value *argv,
55
+ uint8_t *old_sp, Span<uint8_t> *new_stack,
56
+ napi_value (*call)(Napi::Function *func, size_t argc, napi_value *argv));
57
+
58
+ static void *const Trampolines[][2] = {
59
+ { &Trampoline0, &TrampolineX0 },
60
+ { &Trampoline1, &TrampolineX1 },
61
+ { &Trampoline2, &TrampolineX2 },
62
+ { &Trampoline3, &TrampolineX3 },
63
+ { &Trampoline4, &TrampolineX4 },
64
+ { &Trampoline5, &TrampolineX5 },
65
+ { &Trampoline6, &TrampolineX6 },
66
+ { &Trampoline7, &TrampolineX7 },
67
+ { &Trampoline8, &TrampolineX8 },
68
+ { &Trampoline9, &TrampolineX9 },
69
+ { &Trampoline10, &TrampolineX10 },
70
+ { &Trampoline11, &TrampolineX11 },
71
+ { &Trampoline12, &TrampolineX12 },
72
+ { &Trampoline13, &TrampolineX13 },
73
+ { &Trampoline14, &TrampolineX14 },
74
+ { &Trampoline15, &TrampolineX15 }
75
+ };
76
+ RG_STATIC_ASSERT(RG_LEN(Trampolines) == MaxTrampolines);
77
+
78
+ static RG_THREAD_LOCAL CallData *exec_call;
31
79
 
32
80
  static inline bool IsRegular(Size size)
33
81
  {
@@ -88,15 +136,23 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
88
136
  case PrimitiveKind::UInt16:
89
137
  case PrimitiveKind::Int32:
90
138
  case PrimitiveKind::UInt32:
91
- case PrimitiveKind::Int64:
92
- case PrimitiveKind::UInt64: {
139
+ case PrimitiveKind::Int64: {
93
140
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
94
141
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
95
142
  return false;
96
143
  }
97
144
 
98
145
  int64_t v = CopyNumber<int64_t>(value);
99
- *(args_ptr++) = (uint64_t)v;
146
+ *(int64_t *)(args_ptr++) = v;
147
+ } break;
148
+ case PrimitiveKind::UInt64: {
149
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
150
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
151
+ return false;
152
+ }
153
+
154
+ uint64_t v = CopyNumber<uint64_t>(value);
155
+ *(args_ptr++) = v;
100
156
  } break;
101
157
  case PrimitiveKind::String: {
102
158
  const char *str;
@@ -199,20 +255,44 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
199
255
  double d = CopyNumber<double>(value);
200
256
  *(double *)(args_ptr++) = d;
201
257
  } break;
258
+ case PrimitiveKind::Callback: {
259
+ void *ptr;
260
+
261
+ if (value.IsFunction()) {
262
+ Napi::Function func = value.As<Napi::Function>();
263
+
264
+ Size idx = ReserveTrampoline(param.type->proto, func);
265
+ if (RG_UNLIKELY(idx < 0))
266
+ return false;
267
+
268
+ ptr = GetTrampoline(idx, param.type->proto);
269
+ } else if (CheckValueTag(instance, value, param.type)) {
270
+ ptr = value.As<Napi::External<uint8_t>>().Data();
271
+ } else if (IsNullOrUndefined(value)) {
272
+ ptr = nullptr;
273
+ } else {
274
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
275
+ return false;
276
+ }
277
+
278
+ *(void **)(args_ptr++) = ptr;
279
+ } break;
202
280
  }
203
281
  }
204
282
 
205
- sp = mem->stack.end();
283
+ new_sp = mem->stack.end();
206
284
 
207
285
  return true;
208
286
  }
209
287
 
210
288
  void CallData::Execute()
211
289
  {
290
+ exec_call = this;
291
+
212
292
  #define PERFORM_CALL(Suffix) \
213
293
  ([&]() { \
214
- auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, sp) \
215
- : ForwardCall ## Suffix(func->func, sp)); \
294
+ auto ret = (func->forward_fp ? ForwardCallX ## Suffix(func->func, new_sp, &old_sp) \
295
+ : ForwardCall ## Suffix(func->func, new_sp, &old_sp)); \
216
296
  return ret; \
217
297
  })()
218
298
 
@@ -230,7 +310,8 @@ void CallData::Execute()
230
310
  case PrimitiveKind::String:
231
311
  case PrimitiveKind::String16:
232
312
  case PrimitiveKind::Pointer:
233
- case PrimitiveKind::Record: { result.u64 = PERFORM_CALL(G); } break;
313
+ case PrimitiveKind::Record:
314
+ case PrimitiveKind::Callback: { result.u64 = PERFORM_CALL(G); } break;
234
315
  case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
235
316
  case PrimitiveKind::Float32: { result.f = PERFORM_CALL(F); } break;
236
317
  case PrimitiveKind::Float64: { result.d = PERFORM_CALL(D); } break;
@@ -259,11 +340,16 @@ Napi::Value CallData::Complete()
259
340
  case PrimitiveKind::UInt64: return Napi::BigInt::New(env, result.u64);
260
341
  case PrimitiveKind::String: return Napi::String::New(env, (const char *)result.ptr);
261
342
  case PrimitiveKind::String16: return Napi::String::New(env, (const char16_t *)result.ptr);
262
- case PrimitiveKind::Pointer: {
263
- Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
264
- SetValueTag(instance, external, func->ret.type);
265
-
266
- return external;
343
+ case PrimitiveKind::Pointer:
344
+ case PrimitiveKind::Callback: {
345
+ if (result.ptr) {
346
+ Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
347
+ SetValueTag(instance, external, func->ret.type);
348
+
349
+ return external;
350
+ } else {
351
+ return env.Null();
352
+ }
267
353
  } break;
268
354
  case PrimitiveKind::Record: {
269
355
  const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
@@ -280,6 +366,309 @@ Napi::Value CallData::Complete()
280
366
  RG_UNREACHABLE();
281
367
  }
282
368
 
369
+ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
370
+ {
371
+ const FunctionInfo *proto = instance->trampolines[idx].proto;
372
+ Napi::Function func = instance->trampolines[idx].func;
373
+
374
+ // Allow reuse of static trampoline
375
+ instance->free_trampolines |= 1u << idx;
376
+ used_trampolines &= ~(1u << idx);
377
+
378
+ uint64_t *gpr_ptr = (uint64_t *)own_sp;
379
+ uint64_t *xmm_ptr = gpr_ptr + 4;
380
+ uint64_t *args_ptr = (uint64_t *)caller_sp;
381
+
382
+ uint8_t *return_ptr = !proto->ret.regular ? (uint8_t *)gpr_ptr[0] : nullptr;
383
+
384
+ LocalArray<napi_value, MaxParameters> arguments;
385
+
386
+ // Convert to JS arguments
387
+ for (Size i = 0, j = !!return_ptr; i < proto->parameters.len; i++, j++) {
388
+ const ParameterInfo &param = proto->parameters[i];
389
+ RG_ASSERT(param.directions >= 1 && param.directions <= 3);
390
+
391
+ switch (param.type->primitive) {
392
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
393
+
394
+ case PrimitiveKind::Bool: {
395
+ bool b = *(bool *)(j < 4 ? gpr_ptr + j : args_ptr);
396
+ args_ptr += (j >= 4);
397
+
398
+ Napi::Value arg = Napi::Boolean::New(env, b);
399
+ arguments.Append(arg);
400
+ } break;
401
+ case PrimitiveKind::Int8: {
402
+ double d = (double)*(int8_t *)(j < 4 ? gpr_ptr + j : args_ptr);
403
+ args_ptr += (j >= 4);
404
+
405
+ Napi::Value arg = Napi::Number::New(env, d);
406
+ arguments.Append(arg);
407
+ } break;
408
+ case PrimitiveKind::UInt8: {
409
+ double d = (double)*(uint8_t *)(j < 4 ? gpr_ptr + j : args_ptr);
410
+ args_ptr += (j >= 4);
411
+
412
+ Napi::Value arg = Napi::Number::New(env, d);
413
+ arguments.Append(arg);
414
+ } break;
415
+ case PrimitiveKind::Int16: {
416
+ double d = (double)*(int16_t *)(j < 4 ? gpr_ptr + j : args_ptr);
417
+ args_ptr += (j >= 4);
418
+
419
+ Napi::Value arg = Napi::Number::New(env, d);
420
+ arguments.Append(arg);
421
+ } break;
422
+ case PrimitiveKind::UInt16: {
423
+ double d = (double)*(uint16_t *)(j < 4 ? gpr_ptr + j : args_ptr);
424
+ args_ptr += (j >= 4);
425
+
426
+ Napi::Value arg = Napi::Number::New(env, d);
427
+ arguments.Append(arg);
428
+ } break;
429
+ case PrimitiveKind::Int32: {
430
+ double d = (double)*(int32_t *)(j < 4 ? gpr_ptr + j : args_ptr);
431
+ args_ptr += (j >= 4);
432
+
433
+ Napi::Value arg = Napi::Number::New(env, d);
434
+ arguments.Append(arg);
435
+ } break;
436
+ case PrimitiveKind::UInt32: {
437
+ double d = (double)*(int32_t *)(j < 4 ? gpr_ptr + j : args_ptr);
438
+ args_ptr += (j >= 4);
439
+
440
+ Napi::Value arg = Napi::Number::New(env, d);
441
+ arguments.Append(arg);
442
+ } break;
443
+ case PrimitiveKind::Int64: {
444
+ int64_t v = *(int64_t *)(j < 4 ? gpr_ptr + j : args_ptr);
445
+ args_ptr += (j >= 4);
446
+
447
+ Napi::Value arg = Napi::BigInt::New(env, v);
448
+ arguments.Append(arg);
449
+ } break;
450
+ case PrimitiveKind::UInt64: {
451
+ uint64_t v = *(uint64_t *)(j < 4 ? gpr_ptr + j : args_ptr);
452
+ args_ptr += (j >= 4);
453
+
454
+ Napi::Value arg = Napi::BigInt::New(env, v);
455
+ arguments.Append(arg);
456
+ } break;
457
+ case PrimitiveKind::String: {
458
+ const char *str = *(const char **)(j < 4 ? gpr_ptr + j : args_ptr);
459
+ args_ptr += (j >= 4);
460
+
461
+ Napi::Value arg = Napi::String::New(env, str);
462
+ arguments.Append(arg);
463
+ } break;
464
+ case PrimitiveKind::String16: {
465
+ const char16_t *str16 = *(const char16_t **)(j < 4 ? gpr_ptr + j : args_ptr);
466
+ args_ptr += (j >= 4);
467
+
468
+ Napi::Value arg = Napi::String::New(env, str16);
469
+ arguments.Append(arg);
470
+ } break;
471
+ case PrimitiveKind::Pointer:
472
+ case PrimitiveKind::Callback: {
473
+ void *ptr2 = *(void **)(j < 4 ? gpr_ptr + j : args_ptr);
474
+ args_ptr += (j >= 4);
475
+
476
+ if (ptr2) {
477
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
478
+ SetValueTag(instance, external, param.type);
479
+
480
+ arguments.Append(external);
481
+ } else {
482
+ arguments.Append(env.Null());
483
+ }
484
+ } break;
485
+ case PrimitiveKind::Record: {
486
+ uint8_t *ptr;
487
+ if (param.regular) {
488
+ ptr = (uint8_t *)(j < 4 ? gpr_ptr + j : args_ptr);
489
+ } else {
490
+ ptr = *(uint8_t **)(j < 4 ? gpr_ptr + j : args_ptr);
491
+ }
492
+ args_ptr += (j >= 4);
493
+
494
+ Napi::Object obj2 = PopObject(ptr, param.type);
495
+ arguments.Append(obj2);
496
+ } break;
497
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
498
+ case PrimitiveKind::Float32: {
499
+ float f = *(float *)(j < 4 ? xmm_ptr + j : args_ptr);
500
+ args_ptr += (j >= 4);
501
+
502
+ Napi::Value arg = Napi::Number::New(env, (double)f);
503
+ arguments.Append(arg);
504
+ } break;
505
+ case PrimitiveKind::Float64: {
506
+ double d = *(double *)(j < 4 ? xmm_ptr + j : args_ptr);
507
+ args_ptr += (j >= 4);
508
+
509
+ Napi::Value arg = Napi::Number::New(env, d);
510
+ arguments.Append(arg);
511
+ } break;
512
+ }
513
+ }
514
+
515
+ const TypeInfo *type = proto->ret.type;
516
+
517
+ // Make the call
518
+ napi_value ret = CallSwitchStack(&func, (size_t)arguments.len, arguments.data, old_sp, &mem->stack,
519
+ [](Napi::Function *func, size_t argc, napi_value *argv) { return (napi_value)func->Call(argc, argv); });
520
+ Napi::Value value(env, ret);
521
+
522
+ switch (type->primitive) {
523
+ case PrimitiveKind::Void: {} break;
524
+ case PrimitiveKind::Bool: {
525
+ if (RG_UNLIKELY(!value.IsBoolean())) {
526
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected boolean", GetValueType(instance, value));
527
+ return;
528
+ }
529
+
530
+ bool b = value.As<Napi::Boolean>();
531
+ out_reg->rax = (uint64_t)b;
532
+ } break;
533
+ case PrimitiveKind::Int8:
534
+ case PrimitiveKind::UInt8:
535
+ case PrimitiveKind::Int16:
536
+ case PrimitiveKind::UInt16:
537
+ case PrimitiveKind::Int32:
538
+ case PrimitiveKind::UInt32:
539
+ case PrimitiveKind::Int64:
540
+ case PrimitiveKind::UInt64: {
541
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
542
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
543
+ return;
544
+ }
545
+
546
+ int64_t v = CopyNumber<int64_t>(value);
547
+ out_reg->rax = (uint64_t)v;
548
+ } break;
549
+ case PrimitiveKind::String: {
550
+ const char *str;
551
+ if (RG_LIKELY(value.IsString())) {
552
+ str = PushString(value);
553
+ if (RG_UNLIKELY(!str))
554
+ return;
555
+ } else if (IsNullOrUndefined(value)) {
556
+ str = nullptr;
557
+ } else {
558
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
559
+ return;
560
+ }
561
+
562
+ out_reg->rax = (uint64_t)str;
563
+ } break;
564
+ case PrimitiveKind::String16: {
565
+ const char16_t *str16;
566
+ if (RG_LIKELY(value.IsString())) {
567
+ str16 = PushString16(value);
568
+ if (RG_UNLIKELY(!str16))
569
+ return;
570
+ } else if (IsNullOrUndefined(value)) {
571
+ str16 = nullptr;
572
+ } else {
573
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
574
+ return;
575
+ }
576
+
577
+ out_reg->rax = (uint64_t)str16;
578
+ } break;
579
+ case PrimitiveKind::Pointer: {
580
+ uint8_t *ptr;
581
+
582
+ if (CheckValueTag(instance, value, type)) {
583
+ ptr = value.As<Napi::External<uint8_t>>().Data();
584
+ } else if (IsObject(value) && type->ref->primitive == PrimitiveKind::Record) {
585
+ Napi::Object obj = value.As<Napi::Object>();
586
+
587
+ if (RG_UNLIKELY(!AllocHeap(type->ref->size, 16, &ptr)))
588
+ return;
589
+
590
+ if (!PushObject(obj, type->ref, ptr))
591
+ return;
592
+ } else if (IsNullOrUndefined(value)) {
593
+ ptr = nullptr;
594
+ } else {
595
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected %2", GetValueType(instance, value), type->name);
596
+ return;
597
+ }
598
+
599
+ out_reg->rax = (uint64_t)ptr;
600
+ } break;
601
+ case PrimitiveKind::Record: {
602
+ if (RG_UNLIKELY(!IsObject(value))) {
603
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected object", GetValueType(instance, value));
604
+ return;
605
+ }
606
+
607
+ Napi::Object obj = value.As<Napi::Object>();
608
+
609
+ if (return_ptr) {
610
+ if (!PushObject(obj, type, return_ptr))
611
+ return;
612
+ out_reg->rax = (uint64_t)return_ptr;
613
+ } else {
614
+ PushObject(obj, type, (uint8_t *)&out_reg->rax);
615
+ }
616
+ } break;
617
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
618
+ case PrimitiveKind::Float32: {
619
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
620
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
621
+ return;
622
+ }
623
+
624
+ float f = CopyNumber<float>(value);
625
+ memcpy(&out_reg->xmm0, &f, 4);
626
+ } break;
627
+ case PrimitiveKind::Float64: {
628
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
629
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
630
+ return;
631
+ }
632
+
633
+ double d = CopyNumber<double>(value);
634
+ out_reg->xmm0 = d;
635
+ } break;
636
+ case PrimitiveKind::Callback: {
637
+ void *ptr;
638
+
639
+ if (value.IsFunction()) {
640
+ Napi::Function func = value.As<Napi::Function>();
641
+
642
+ Size idx = ReserveTrampoline(type->proto, func);
643
+ if (RG_UNLIKELY(idx < 0))
644
+ return;
645
+
646
+ ptr = GetTrampoline(idx, type->proto);
647
+ } else if (CheckValueTag(instance, value, type)) {
648
+ ptr = value.As<Napi::External<uint8_t>>().Data();
649
+ } else if (IsNullOrUndefined(value)) {
650
+ ptr = nullptr;
651
+ } else {
652
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected %2", GetValueType(instance, value), type->name);
653
+ return;
654
+ }
655
+
656
+ out_reg->rax = (uint64_t)ptr;
657
+ } break;
658
+ }
659
+ }
660
+
661
+ void *GetTrampoline(Size idx, const FunctionInfo *proto)
662
+ {
663
+ bool xmm = proto->forward_fp || IsFloat(proto->ret.type);
664
+ return Trampolines[idx][xmm];
665
+ }
666
+
667
+ extern "C" void RelayCallBack(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
668
+ {
669
+ exec_call->Relay(idx, own_sp, caller_sp, out_reg);
670
+ }
671
+
283
672
  }
284
673
 
285
674
  #endif