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.
package/src/abi_x86.cc CHANGED
@@ -22,12 +22,63 @@
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 ForwardCallRG(const void *func, uint8_t *sp);
29
- extern "C" float ForwardCallRF(const void *func, uint8_t *sp);
30
- extern "C" double ForwardCallRD(const void *func, uint8_t *sp);
25
+ struct BackRegisters {
26
+ uint32_t eax;
27
+ uint32_t edx;
28
+ double d;
29
+ float f;
30
+ bool is_double;
31
+ };
32
+
33
+ extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
34
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
35
+ extern "C" double ForwardCallD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
36
+ extern "C" uint64_t ForwardCallRG(const void *func, uint8_t *sp, uint8_t **out_old_sp);
37
+ extern "C" float ForwardCallRF(const void *func, uint8_t *sp, uint8_t **out_old_sp);
38
+ extern "C" double ForwardCallRD(const void *func, uint8_t *sp, uint8_t **out_old_sp);
39
+
40
+ extern "C" int Trampoline0; extern "C" int TrampolineX0;
41
+ extern "C" int Trampoline1; extern "C" int TrampolineX1;
42
+ extern "C" int Trampoline2; extern "C" int TrampolineX2;
43
+ extern "C" int Trampoline3; extern "C" int TrampolineX3;
44
+ extern "C" int Trampoline4; extern "C" int TrampolineX4;
45
+ extern "C" int Trampoline5; extern "C" int TrampolineX5;
46
+ extern "C" int Trampoline6; extern "C" int TrampolineX6;
47
+ extern "C" int Trampoline7; extern "C" int TrampolineX7;
48
+ extern "C" int Trampoline8; extern "C" int TrampolineX8;
49
+ extern "C" int Trampoline9; extern "C" int TrampolineX9;
50
+ extern "C" int Trampoline10; extern "C" int TrampolineX10;
51
+ extern "C" int Trampoline11; extern "C" int TrampolineX11;
52
+ extern "C" int Trampoline12; extern "C" int TrampolineX12;
53
+ extern "C" int Trampoline13; extern "C" int TrampolineX13;
54
+ extern "C" int Trampoline14; extern "C" int TrampolineX14;
55
+ extern "C" int Trampoline15; extern "C" int TrampolineX15;
56
+
57
+ extern "C" napi_value CallSwitchStack(Napi::Function *func, size_t argc, napi_value *argv,
58
+ uint8_t *old_sp, Span<uint8_t> *new_stack,
59
+ napi_value (*call)(Napi::Function *func, size_t argc, napi_value *argv));
60
+
61
+ static void *const Trampolines[][2] = {
62
+ { &Trampoline0, &TrampolineX0 },
63
+ { &Trampoline1, &TrampolineX1 },
64
+ { &Trampoline2, &TrampolineX2 },
65
+ { &Trampoline3, &TrampolineX3 },
66
+ { &Trampoline4, &TrampolineX4 },
67
+ { &Trampoline5, &TrampolineX5 },
68
+ { &Trampoline6, &TrampolineX6 },
69
+ { &Trampoline7, &TrampolineX7 },
70
+ { &Trampoline8, &TrampolineX8 },
71
+ { &Trampoline9, &TrampolineX9 },
72
+ { &Trampoline10, &TrampolineX10 },
73
+ { &Trampoline11, &TrampolineX11 },
74
+ { &Trampoline12, &TrampolineX12 },
75
+ { &Trampoline13, &TrampolineX13 },
76
+ { &Trampoline14, &TrampolineX14 },
77
+ { &Trampoline15, &TrampolineX15 }
78
+ };
79
+ RG_STATIC_ASSERT(RG_LEN(Trampolines) == MaxTrampolines);
80
+
81
+ static RG_THREAD_LOCAL CallData *exec_call;
31
82
 
32
83
  static inline bool IsRegular(Size size)
33
84
  {
@@ -129,25 +180,42 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
129
180
  case PrimitiveKind::UInt8:
130
181
  case PrimitiveKind::Int16:
131
182
  case PrimitiveKind::UInt16:
132
- case PrimitiveKind::Int32:
133
- case PrimitiveKind::UInt32: {
183
+ case PrimitiveKind::Int32: {
134
184
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
135
185
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
136
186
  return false;
137
187
  }
138
188
 
139
189
  int32_t v = CopyNumber<int32_t>(value);
140
- *((param.fast ? fast_ptr : args_ptr)++) = (uint32_t)v;
190
+ *(int32_t *)((param.fast ? fast_ptr : args_ptr)++) = v;
141
191
  } break;
142
- case PrimitiveKind::Int64:
143
- case PrimitiveKind::UInt64: {
192
+ case PrimitiveKind::UInt32: {
193
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
194
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
195
+ return false;
196
+ }
197
+
198
+ uint32_t v = CopyNumber<uint32_t>(value);
199
+ *((param.fast ? fast_ptr : args_ptr)++) = v;
200
+ } break;
201
+ case PrimitiveKind::Int64: {
144
202
  if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
145
203
  ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
146
204
  return false;
147
205
  }
148
206
 
149
207
  int64_t v = CopyNumber<int64_t>(value);
150
- *(uint64_t *)args_ptr = (uint64_t)v;
208
+ *(int64_t *)args_ptr = v;
209
+ args_ptr += 2;
210
+ } break;
211
+ case PrimitiveKind::UInt64: {
212
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
213
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
214
+ return false;
215
+ }
216
+
217
+ uint64_t v = CopyNumber<uint64_t>(value);
218
+ *(uint64_t *)args_ptr = v;
151
219
  args_ptr += 2;
152
220
  } break;
153
221
  case PrimitiveKind::String: {
@@ -252,20 +320,44 @@ bool CallData::Prepare(const Napi::CallbackInfo &info)
252
320
  *(double *)args_ptr = d;
253
321
  args_ptr += 2;
254
322
  } break;
323
+ case PrimitiveKind::Callback: {
324
+ void *ptr;
325
+
326
+ if (value.IsFunction()) {
327
+ Napi::Function func = value.As<Napi::Function>();
328
+
329
+ Size idx = ReserveTrampoline(param.type->proto, func);
330
+ if (RG_UNLIKELY(idx < 0))
331
+ return false;
332
+
333
+ ptr = GetTrampoline(idx, param.type->proto);
334
+ } else if (CheckValueTag(instance, value, param.type)) {
335
+ ptr = value.As<Napi::External<uint8_t>>().Data();
336
+ } else if (IsNullOrUndefined(value)) {
337
+ ptr = nullptr;
338
+ } else {
339
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
340
+ return false;
341
+ }
342
+
343
+ *(void **)((param.fast ? fast_ptr : args_ptr)++) = ptr;
344
+ } break;
255
345
  }
256
346
  }
257
347
 
258
- sp = mem->stack.end();
348
+ new_sp = mem->stack.end();
259
349
 
260
350
  return true;
261
351
  }
262
352
 
263
353
  void CallData::Execute()
264
354
  {
355
+ exec_call = this;
356
+
265
357
  #define PERFORM_CALL(Suffix) \
266
358
  ([&]() { \
267
- auto ret = (func->fast ? ForwardCallR ## Suffix(func->func, sp) \
268
- : ForwardCall ## Suffix(func->func, sp)); \
359
+ auto ret = (func->fast ? ForwardCallR ## Suffix(func->func, new_sp, &old_sp) \
360
+ : ForwardCall ## Suffix(func->func, new_sp, &old_sp)); \
269
361
  return ret; \
270
362
  })()
271
363
 
@@ -284,7 +376,8 @@ void CallData::Execute()
284
376
  case PrimitiveKind::String:
285
377
  case PrimitiveKind::String16:
286
378
  case PrimitiveKind::Pointer:
287
- case PrimitiveKind::Record: { result.u64 = PERFORM_CALL(G); } break;
379
+ case PrimitiveKind::Record:
380
+ case PrimitiveKind::Callback: { result.u64 = PERFORM_CALL(G); } break;
288
381
  case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
289
382
  case PrimitiveKind::Float32: { result.f = PERFORM_CALL(F); } break;
290
383
  case PrimitiveKind::Float64: { result.d = PERFORM_CALL(D); } break;
@@ -313,11 +406,16 @@ Napi::Value CallData::Complete()
313
406
  case PrimitiveKind::UInt64: return Napi::BigInt::New(env, result.u64);
314
407
  case PrimitiveKind::String: return Napi::String::New(env, (const char *)result.ptr);
315
408
  case PrimitiveKind::String16: return Napi::String::New(env, (const char16_t *)result.ptr);
316
- case PrimitiveKind::Pointer: {
317
- Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
318
- SetValueTag(instance, external, func->ret.type);
319
-
320
- return external;
409
+ case PrimitiveKind::Pointer:
410
+ case PrimitiveKind::Callback: {
411
+ if (result.ptr) {
412
+ Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
413
+ SetValueTag(instance, external, func->ret.type);
414
+
415
+ return external;
416
+ } else {
417
+ return env.Null();
418
+ }
321
419
  } break;
322
420
  case PrimitiveKind::Record: {
323
421
  const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
@@ -334,6 +432,299 @@ Napi::Value CallData::Complete()
334
432
  RG_UNREACHABLE();
335
433
  }
336
434
 
435
+ void CallData::Relay(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
436
+ {
437
+ const FunctionInfo *proto = instance->trampolines[idx].proto;
438
+ Napi::Function func = instance->trampolines[idx].func;
439
+
440
+ // Allow reuse of static trampoline
441
+ instance->free_trampolines |= 1u << idx;
442
+ used_trampolines &= ~(1u << idx);
443
+
444
+ uint32_t *args_ptr = (uint32_t *)caller_sp;
445
+ uint8_t *return_ptr = !proto->ret.trivial ? (uint8_t *)args_ptr[0] : nullptr;
446
+
447
+ LocalArray<napi_value, MaxParameters> arguments;
448
+
449
+ // Convert to JS arguments
450
+ for (Size i = 0; i < proto->parameters.len; i++) {
451
+ const ParameterInfo &param = proto->parameters[i];
452
+ RG_ASSERT(param.directions >= 1 && param.directions <= 3);
453
+
454
+ switch (param.type->primitive) {
455
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
456
+
457
+ case PrimitiveKind::Bool: {
458
+ bool b = *(bool *)(args_ptr++);
459
+
460
+ Napi::Value arg = Napi::Boolean::New(env, b);
461
+ arguments.Append(arg);
462
+ } break;
463
+ case PrimitiveKind::Int8: {
464
+ double d = (double)*(int8_t *)(args_ptr++);
465
+
466
+ Napi::Value arg = Napi::Number::New(env, d);
467
+ arguments.Append(arg);
468
+ } break;
469
+ case PrimitiveKind::UInt8: {
470
+ double d = (double)*(uint8_t *)(args_ptr++);
471
+
472
+ Napi::Value arg = Napi::Number::New(env, d);
473
+ arguments.Append(arg);
474
+ } break;
475
+ case PrimitiveKind::Int16: {
476
+ double d = (double)*(int16_t *)(args_ptr++);
477
+
478
+ Napi::Value arg = Napi::Number::New(env, d);
479
+ arguments.Append(arg);
480
+ } break;
481
+ case PrimitiveKind::UInt16: {
482
+ double d = (double)*(uint16_t *)(args_ptr++);
483
+
484
+ Napi::Value arg = Napi::Number::New(env, d);
485
+ arguments.Append(arg);
486
+ } break;
487
+ case PrimitiveKind::Int32: {
488
+ double d = (double)*(int32_t *)(args_ptr++);
489
+
490
+ Napi::Value arg = Napi::Number::New(env, d);
491
+ arguments.Append(arg);
492
+ } break;
493
+ case PrimitiveKind::UInt32: {
494
+ double d = (double)*(int32_t *)(args_ptr++);
495
+
496
+ Napi::Value arg = Napi::Number::New(env, d);
497
+ arguments.Append(arg);
498
+ } break;
499
+ case PrimitiveKind::Int64: {
500
+ int64_t v = *(int64_t *)args_ptr;
501
+ args_ptr += 2;
502
+
503
+ Napi::Value arg = Napi::BigInt::New(env, v);
504
+ arguments.Append(arg);
505
+ } break;
506
+ case PrimitiveKind::UInt64: {
507
+ uint64_t v = *(uint64_t *)args_ptr;
508
+ args_ptr += 2;
509
+
510
+ Napi::Value arg = Napi::BigInt::New(env, v);
511
+ arguments.Append(arg);
512
+ } break;
513
+ case PrimitiveKind::String: {
514
+ const char *str = *(const char **)(args_ptr++);
515
+
516
+ Napi::Value arg = Napi::String::New(env, str);
517
+ arguments.Append(arg);
518
+ } break;
519
+ case PrimitiveKind::String16: {
520
+ const char16_t *str16 = *(const char16_t **)(args_ptr++);
521
+
522
+ Napi::Value arg = Napi::String::New(env, str16);
523
+ arguments.Append(arg);
524
+ } break;
525
+ case PrimitiveKind::Pointer:
526
+ case PrimitiveKind::Callback: {
527
+ void *ptr2 = *(void **)(args_ptr++);
528
+
529
+ if (ptr2) {
530
+ Napi::External<void> external = Napi::External<void>::New(env, ptr2);
531
+ SetValueTag(instance, external, param.type);
532
+
533
+ arguments.Append(external);
534
+ } else {
535
+ arguments.Append(env.Null());
536
+ }
537
+ } break;
538
+ case PrimitiveKind::Record: {
539
+ RG_ASSERT(!param.fast);
540
+
541
+ uint8_t *ptr = AlignUp((uint8_t *)args_ptr, param.type->align);
542
+ args_ptr = (uint32_t *)AlignUp(ptr + param.type->size, 4);
543
+ Napi::Object obj2 = PopObject(ptr, param.type);
544
+ arguments.Append(obj2);
545
+ } break;
546
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
547
+ case PrimitiveKind::Float32: {
548
+ float f = *(float *)(args_ptr++);
549
+
550
+ Napi::Value arg = Napi::Number::New(env, (double)f);
551
+ arguments.Append(arg);
552
+ } break;
553
+ case PrimitiveKind::Float64: {
554
+ double d = *(double *)args_ptr;
555
+ args_ptr += 2;
556
+
557
+ Napi::Value arg = Napi::Number::New(env, d);
558
+ arguments.Append(arg);
559
+ } break;
560
+ }
561
+ }
562
+
563
+ const TypeInfo *type = proto->ret.type;
564
+
565
+ // Make the call
566
+ napi_value ret = CallSwitchStack(&func, (size_t)arguments.len, arguments.data, old_sp, &mem->stack,
567
+ [](Napi::Function *func, size_t argc, napi_value *argv) { return (napi_value)func->Call(argc, argv); });
568
+ Napi::Value value(env, ret);
569
+
570
+ switch (type->primitive) {
571
+ case PrimitiveKind::Void: {} break;
572
+ case PrimitiveKind::Bool: {
573
+ if (RG_UNLIKELY(!value.IsBoolean())) {
574
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected boolean", GetValueType(instance, value));
575
+ return;
576
+ }
577
+
578
+ bool b = value.As<Napi::Boolean>();
579
+ out_reg->eax = (uint32_t)b;
580
+ } break;
581
+ case PrimitiveKind::Int8:
582
+ case PrimitiveKind::UInt8:
583
+ case PrimitiveKind::Int16:
584
+ case PrimitiveKind::UInt16:
585
+ case PrimitiveKind::Int32:
586
+ case PrimitiveKind::UInt32: {
587
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
588
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
589
+ return;
590
+ }
591
+
592
+ int32_t v = CopyNumber<int32_t>(value);
593
+ out_reg->eax = (uint32_t)v;
594
+ } break;
595
+ case PrimitiveKind::Int64:
596
+ case PrimitiveKind::UInt64: {
597
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
598
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
599
+ return;
600
+ }
601
+
602
+ int64_t v = CopyNumber<int64_t>(value);
603
+ out_reg->eax = (uint32_t)(v & 0xFFFFFFFFul);
604
+ out_reg->edx = (uint32_t)(v << 32);
605
+ } break;
606
+ case PrimitiveKind::String: {
607
+ const char *str;
608
+ if (RG_LIKELY(value.IsString())) {
609
+ str = PushString(value);
610
+ if (RG_UNLIKELY(!str))
611
+ return;
612
+ } else if (IsNullOrUndefined(value)) {
613
+ str = nullptr;
614
+ } else {
615
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
616
+ return;
617
+ }
618
+
619
+ out_reg->eax = (uint32_t)str;
620
+ } break;
621
+ case PrimitiveKind::String16: {
622
+ const char16_t *str16;
623
+ if (RG_LIKELY(value.IsString())) {
624
+ str16 = PushString16(value);
625
+ if (RG_UNLIKELY(!str16))
626
+ return;
627
+ } else if (IsNullOrUndefined(value)) {
628
+ str16 = nullptr;
629
+ } else {
630
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected string", GetValueType(instance, value));
631
+ return;
632
+ }
633
+
634
+ out_reg->eax = (uint32_t)str16;
635
+ } break;
636
+ case PrimitiveKind::Pointer: {
637
+ uint8_t *ptr;
638
+
639
+ if (CheckValueTag(instance, value, type)) {
640
+ ptr = value.As<Napi::External<uint8_t>>().Data();
641
+ } else if (IsObject(value) && type->ref->primitive == PrimitiveKind::Record) {
642
+ Napi::Object obj = value.As<Napi::Object>();
643
+
644
+ if (RG_UNLIKELY(!AllocHeap(type->ref->size, 16, &ptr)))
645
+ return;
646
+
647
+ if (!PushObject(obj, type->ref, ptr))
648
+ return;
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->eax = (uint32_t)ptr;
657
+ } break;
658
+ case PrimitiveKind::Record: {
659
+ if (RG_UNLIKELY(!IsObject(value))) {
660
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected object", GetValueType(instance, value));
661
+ return;
662
+ }
663
+
664
+ Napi::Object obj = value.As<Napi::Object>();
665
+
666
+ if (return_ptr) {
667
+ if (!PushObject(obj, type, return_ptr))
668
+ return;
669
+ out_reg->eax = (uint32_t)return_ptr;
670
+ } else {
671
+ PushObject(obj, type, (uint8_t *)&out_reg->eax);
672
+ }
673
+ } break;
674
+ case PrimitiveKind::Array: { RG_UNREACHABLE(); } break;
675
+ case PrimitiveKind::Float32: {
676
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
677
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
678
+ return;
679
+ }
680
+
681
+ out_reg->f = CopyNumber<float>(value);
682
+ } break;
683
+ case PrimitiveKind::Float64: {
684
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
685
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected number", GetValueType(instance, value));
686
+ return;
687
+ }
688
+
689
+ out_reg->d = CopyNumber<double>(value);
690
+ out_reg->is_double = true;
691
+ } break;
692
+ case PrimitiveKind::Callback: {
693
+ void *ptr;
694
+
695
+ if (value.IsFunction()) {
696
+ Napi::Function func = value.As<Napi::Function>();
697
+
698
+ Size idx = ReserveTrampoline(type->proto, func);
699
+ if (RG_UNLIKELY(idx < 0))
700
+ return;
701
+
702
+ ptr = GetTrampoline(idx, type->proto);
703
+ } else if (CheckValueTag(instance, value, type)) {
704
+ ptr = value.As<Napi::External<uint8_t>>().Data();
705
+ } else if (IsNullOrUndefined(value)) {
706
+ ptr = nullptr;
707
+ } else {
708
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for return value, expected %2", GetValueType(instance, value), type->name);
709
+ return;
710
+ }
711
+
712
+ out_reg->eax = (uint32_t)ptr;
713
+ } break;
714
+ }
715
+ }
716
+
717
+ void *GetTrampoline(Size idx, const FunctionInfo *proto)
718
+ {
719
+ bool x87 = IsFloat(proto->ret.type);
720
+ return Trampolines[idx][x87];
721
+ }
722
+
723
+ extern "C" void RelayCallBack(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
724
+ {
725
+ exec_call->Relay(idx, own_sp, caller_sp, out_reg);
726
+ }
727
+
337
728
  }
338
729
 
339
730
  #endif