koffi 1.0.0 → 1.0.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.
Files changed (49) hide show
  1. package/CMakeLists.txt +12 -11
  2. package/README.md +19 -17
  3. package/build/qemu/1.0.3/koffi_darwin_x64.tar.gz +0 -0
  4. package/build/qemu/1.0.3/koffi_freebsd_arm64.tar.gz +0 -0
  5. package/build/qemu/1.0.3/koffi_freebsd_ia32.tar.gz +0 -0
  6. package/build/qemu/1.0.3/koffi_freebsd_x64.tar.gz +0 -0
  7. package/build/qemu/1.0.3/koffi_linux_arm.tar.gz +0 -0
  8. package/build/qemu/1.0.3/koffi_linux_arm64.tar.gz +0 -0
  9. package/build/qemu/1.0.3/koffi_linux_ia32.tar.gz +0 -0
  10. package/build/qemu/1.0.3/koffi_linux_x64.tar.gz +0 -0
  11. package/build/qemu/1.0.3/koffi_win32_ia32.tar.gz +0 -0
  12. package/build/qemu/1.0.3/koffi_win32_x64.tar.gz +0 -0
  13. package/package.json +8 -4
  14. package/qemu/qemu.js +794 -0
  15. package/qemu/registry/machines.json +415 -0
  16. package/qemu/registry/sha256sum.txt +45 -0
  17. package/src/{call_arm32.cc → abi_arm32.cc} +76 -50
  18. package/src/{call_arm32_fwd.S → abi_arm32_fwd.S} +0 -0
  19. package/src/{call_arm64.cc → abi_arm64.cc} +79 -51
  20. package/src/{call_arm64_fwd.S → abi_arm64_fwd.S} +0 -0
  21. package/src/{call_x64_sysv.cc → abi_x64_sysv.cc} +77 -49
  22. package/src/{call_x64_sysv_fwd.S → abi_x64_sysv_fwd.S} +0 -0
  23. package/src/{call_x64_win.cc → abi_x64_win.cc} +71 -47
  24. package/src/{call_x64_win_fwd.asm → abi_x64_win_fwd.asm} +0 -0
  25. package/src/{call_x86.cc → abi_x86.cc} +74 -56
  26. package/src/{call_x86_fwd.S → abi_x86_fwd.S} +0 -0
  27. package/src/{call_x86_fwd.asm → abi_x86_fwd.asm} +0 -0
  28. package/src/call.cc +228 -0
  29. package/src/call.hh +96 -4
  30. package/src/ffi.cc +8 -3
  31. package/src/ffi.hh +2 -0
  32. package/src/util.cc +11 -157
  33. package/src/util.hh +0 -91
  34. package/test/CMakeLists.txt +1 -0
  35. package/test/misc.c +289 -0
  36. package/test/misc.def +3 -0
  37. package/test/misc.js +180 -0
  38. package/test/raylib.js +165 -0
  39. package/test/sqlite.js +104 -0
  40. package/build/qemu/1.0.0/koffi_darwin_x64.tar.gz +0 -0
  41. package/build/qemu/1.0.0/koffi_freebsd_arm64.tar.gz +0 -0
  42. package/build/qemu/1.0.0/koffi_freebsd_ia32.tar.gz +0 -0
  43. package/build/qemu/1.0.0/koffi_freebsd_x64.tar.gz +0 -0
  44. package/build/qemu/1.0.0/koffi_linux_arm.tar.gz +0 -0
  45. package/build/qemu/1.0.0/koffi_linux_arm64.tar.gz +0 -0
  46. package/build/qemu/1.0.0/koffi_linux_ia32.tar.gz +0 -0
  47. package/build/qemu/1.0.0/koffi_linux_x64.tar.gz +0 -0
  48. package/build/qemu/1.0.0/koffi_win32_ia32.tar.gz +0 -0
  49. package/build/qemu/1.0.0/koffi_win32_x64.tar.gz +0 -0
@@ -82,11 +82,8 @@ bool AnalyseFunction(InstanceData *instance, FunctionInfo *func)
82
82
  return true;
83
83
  }
84
84
 
85
- Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info)
85
+ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
86
86
  {
87
- Napi::Env env = info.Env();
88
- CallData call(env, instance, func);
89
-
90
87
  // Sanity checks
91
88
  if (info.Length() < (uint32_t)func->parameters.len) {
92
89
  ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
@@ -98,14 +95,14 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
98
95
  uint32_t *fast_ptr = nullptr;
99
96
 
100
97
  // Pass return value in register or through memory
101
- if (RG_UNLIKELY(!call.AllocStack(func->args_size, 16, &args_ptr)))
98
+ if (RG_UNLIKELY(!AllocStack(func->args_size, 16, &args_ptr)))
102
99
  return env.Null();
103
100
  if (func->convention == CallConvention::Fastcall) {
104
101
  fast_ptr = args_ptr;
105
102
  args_ptr += 4;
106
103
  }
107
104
  if (!func->ret.trivial) {
108
- if (RG_UNLIKELY(!call.AllocHeap(func->ret.type->size, 16, &return_ptr)))
105
+ if (RG_UNLIKELY(!AllocHeap(func->ret.type->size, 16, &return_ptr)))
109
106
  return env.Null();
110
107
  *((func->ret.fast ? fast_ptr : args_ptr)++) = (uint32_t)return_ptr;
111
108
  }
@@ -178,7 +175,7 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
178
175
  case PrimitiveKind::String: {
179
176
  const char *str;
180
177
  if (RG_LIKELY(value.IsString())) {
181
- str = call.PushString(value);
178
+ str = PushString(value);
182
179
  if (RG_UNLIKELY(!str))
183
180
  return env.Null();
184
181
  } else if (IsNullOrUndefined(value)) {
@@ -190,6 +187,21 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
190
187
 
191
188
  *(const char **)((param.fast ? fast_ptr : args_ptr)++) = str;
192
189
  } break;
190
+ case PrimitiveKind::String16: {
191
+ const char16_t *str16;
192
+ if (RG_LIKELY(value.IsString())) {
193
+ str16 = PushString16(value);
194
+ if (RG_UNLIKELY(!str16))
195
+ return env.Null();
196
+ } else if (IsNullOrUndefined(value)) {
197
+ str16 = nullptr;
198
+ } else {
199
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
200
+ return env.Null();
201
+ }
202
+
203
+ *(const char16_t **)((param.fast ? fast_ptr : args_ptr)++) = str16;
204
+ } break;
193
205
  case PrimitiveKind::Pointer: {
194
206
  uint8_t *ptr;
195
207
 
@@ -198,11 +210,11 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
198
210
  } else if (IsObject(value) && param.type->ref->primitive == PrimitiveKind::Record) {
199
211
  Napi::Object obj = value.As<Napi::Object>();
200
212
 
201
- if (RG_UNLIKELY(!call.AllocHeap(param.type->ref->size, 16, &ptr)))
213
+ if (RG_UNLIKELY(!AllocHeap(param.type->ref->size, 16, &ptr)))
202
214
  return env.Null();
203
215
 
204
216
  if (param.directions & 1) {
205
- if (!call.PushObject(obj, param.type->ref, ptr))
217
+ if (!PushObject(obj, param.type->ref, ptr))
206
218
  return env.Null();
207
219
  } else {
208
220
  memset(ptr, 0, param.type->size);
@@ -231,11 +243,11 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
231
243
 
232
244
  if (param.fast) {
233
245
  uint8_t *ptr = (uint8_t *)(fast_ptr++);
234
- if (!call.PushObject(obj, param.type, ptr))
246
+ if (!PushObject(obj, param.type, ptr))
235
247
  return env.Null();
236
248
  } else {
237
249
  uint8_t *ptr = (uint8_t *)AlignUp(args_ptr, param.type->align);
238
- if (!call.PushObject(obj, param.type, ptr))
250
+ if (!PushObject(obj, param.type, ptr))
239
251
  return env.Null();
240
252
  args_ptr = (uint32_t *)AlignUp(ptr + param.type->size, 4);
241
253
  }
@@ -244,70 +256,76 @@ Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, cons
244
256
  }
245
257
 
246
258
  if (instance->debug) {
247
- call.DumpDebug();
259
+ DumpDebug();
248
260
  }
249
261
 
250
262
  #define PERFORM_CALL(Suffix) \
251
263
  ([&]() { \
252
- auto ret = (func->convention == CallConvention::Fastcall ? ForwardCallR ## Suffix(func->func, call.GetSP()) \
253
- : ForwardCall ## Suffix(func->func, call.GetSP())); \
264
+ auto ret = (func->convention == CallConvention::Fastcall ? ForwardCallR ## Suffix(func->func, GetSP()) \
265
+ : ForwardCall ## Suffix(func->func, GetSP())); \
254
266
  PopOutArguments(out_objects); \
255
267
  return ret; \
256
268
  })()
257
269
 
258
270
  // Execute and convert return value
259
271
  switch (func->ret.type->primitive) {
272
+ case PrimitiveKind::Void: {
273
+ PERFORM_CALL(G);
274
+ return env.Null();
275
+ } break;
276
+ case PrimitiveKind::Bool: {
277
+ uint32_t rax = (uint32_t)PERFORM_CALL(G);
278
+ return Napi::Boolean::New(env, rax);
279
+ } break;
280
+ case PrimitiveKind::Int8:
281
+ case PrimitiveKind::UInt8:
282
+ case PrimitiveKind::Int16:
283
+ case PrimitiveKind::UInt16:
284
+ case PrimitiveKind::Int32:
285
+ case PrimitiveKind::UInt32: {
286
+ uint32_t rax = (uint32_t)PERFORM_CALL(G);
287
+ return Napi::Number::New(env, (double)rax);
288
+ } break;
289
+ case PrimitiveKind::Int64: {
290
+ uint64_t ret = PERFORM_CALL(G);
291
+ return Napi::BigInt::New(env, (int64_t)ret);
292
+ } break;
293
+ case PrimitiveKind::UInt64: {
294
+ uint64_t ret = PERFORM_CALL(G);
295
+ return Napi::BigInt::New(env, ret);
296
+ } break;
260
297
  case PrimitiveKind::Float32: {
261
298
  float f = PERFORM_CALL(F);
262
-
263
299
  return Napi::Number::New(env, (double)f);
264
300
  } break;
265
-
266
301
  case PrimitiveKind::Float64: {
267
302
  double d = PERFORM_CALL(D);
268
-
269
303
  return Napi::Number::New(env, d);
270
304
  } break;
305
+ case PrimitiveKind::String: {
306
+ uint32_t rax = (uint32_t)PERFORM_CALL(G);
307
+ return Napi::String::New(env, (const char *)rax);
308
+ } break;
309
+ case PrimitiveKind::String16: {
310
+ uint32_t rax = (uint32_t)PERFORM_CALL(G);
311
+ return Napi::String::New(env, (const char16_t *)rax);
312
+ } break;
313
+ case PrimitiveKind::Pointer: {
314
+ uint32_t rax = (uint32_t)PERFORM_CALL(G);
315
+ void *ptr = (void *)rax;
316
+
317
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
318
+ SetValueTag(instance, external, func->ret.type);
319
+
320
+ return external;
321
+ } break;
322
+
323
+ case PrimitiveKind::Record: {
324
+ uint64_t ret = PERFORM_CALL(G);
325
+ const uint8_t *ptr = return_ptr ? return_ptr : (const uint8_t *)&ret;
271
326
 
272
- default: {
273
- // We can't directly use the struct as a return value, because not all platforms
274
- // treat it the same: it is trivial only on Windows (see AnalyseFunction).
275
- uint64_t raw = PERFORM_CALL(G);
276
- struct {
277
- uint32_t rax;
278
- uint32_t rdx;
279
- } ret;
280
- memcpy(&ret, &raw, RG_SIZE(raw));
281
-
282
- switch (func->ret.type->primitive) {
283
- case PrimitiveKind::Void: return env.Null();
284
- case PrimitiveKind::Bool: return Napi::Boolean::New(env, ret.rax);
285
- case PrimitiveKind::Int8: return Napi::Number::New(env, (double)ret.rax);
286
- case PrimitiveKind::UInt8: return Napi::Number::New(env, (double)ret.rax);
287
- case PrimitiveKind::Int16: return Napi::Number::New(env, (double)ret.rax);
288
- case PrimitiveKind::UInt16: return Napi::Number::New(env, (double)ret.rax);
289
- case PrimitiveKind::Int32: return Napi::Number::New(env, (double)ret.rax);
290
- case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)ret.rax);
291
- case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)raw);
292
- case PrimitiveKind::UInt64: return Napi::BigInt::New(env, raw);
293
- case PrimitiveKind::Float32: { RG_UNREACHABLE(); } break;
294
- case PrimitiveKind::Float64: { RG_UNREACHABLE(); } break;
295
- case PrimitiveKind::String: return Napi::String::New(env, (const char *)ret.rax);
296
- case PrimitiveKind::Pointer: {
297
- void *ptr = (void *)ret.rax;
298
-
299
- Napi::External<void> external = Napi::External<void>::New(env, ptr);
300
- SetValueTag(instance, external, func->ret.type);
301
-
302
- return external;
303
- } break;
304
-
305
- case PrimitiveKind::Record: {
306
- const uint8_t *ptr = return_ptr ? return_ptr : (const uint8_t *)&ret;
307
- Napi::Object obj = PopObject(env, ptr, func->ret.type);
308
- return obj;
309
- } break;
310
- }
327
+ Napi::Object obj = PopObject(env, ptr, func->ret.type);
328
+ return obj;
311
329
  } break;
312
330
  }
313
331
 
File without changes
File without changes
package/src/call.cc ADDED
@@ -0,0 +1,228 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #include "vendor/libcc/libcc.hh"
15
+ #include "call.hh"
16
+ #include "ffi.hh"
17
+ #include "util.hh"
18
+
19
+ #include <napi.h>
20
+
21
+ namespace RG {
22
+
23
+ CallData::CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func)
24
+ : env(env), instance(instance), func(func),
25
+ stack_mem(&instance->stack_mem), heap_mem(&instance->heap_mem),
26
+ old_stack_mem(instance->stack_mem), old_heap_mem(instance->heap_mem)
27
+ {
28
+ RG_ASSERT(AlignUp(stack_mem->ptr, 16) == stack_mem->ptr);
29
+ RG_ASSERT(AlignUp(stack_mem->end(), 16) == stack_mem->end());
30
+ }
31
+
32
+ CallData::~CallData()
33
+ {
34
+ instance->stack_mem = old_stack_mem;
35
+ instance->heap_mem = old_heap_mem;
36
+ }
37
+
38
+ const char *CallData::PushString(const Napi::Value &value)
39
+ {
40
+ RG_ASSERT(value.IsString());
41
+
42
+ Napi::Env env = value.Env();
43
+
44
+ Span<char> buf;
45
+ size_t len = 0;
46
+ napi_status status;
47
+
48
+ buf.ptr = (char *)heap_mem->ptr;
49
+ buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32));
50
+
51
+ status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
52
+ RG_ASSERT(status == napi_ok);
53
+
54
+ len++;
55
+
56
+ if (RG_LIKELY(len < (size_t)buf.len)) {
57
+ heap_mem->ptr += (Size)len;
58
+ heap_mem->len -= (Size)len;
59
+ } else {
60
+ status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
61
+ RG_ASSERT(status == napi_ok);
62
+
63
+ len++;
64
+
65
+ buf.ptr = (char *)Allocator::Allocate(&big_alloc, (Size)len);
66
+ buf.len = (Size)len;
67
+
68
+ status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
69
+ RG_ASSERT(status == napi_ok);
70
+ }
71
+
72
+ return buf.ptr;
73
+ }
74
+
75
+ const char16_t *CallData::PushString16(const Napi::Value &value)
76
+ {
77
+ RG_ASSERT(value.IsString());
78
+
79
+ Napi::Env env = value.Env();
80
+
81
+ Span<char16_t> buf;
82
+ size_t len = 0;
83
+ napi_status status;
84
+
85
+ buf.ptr = (char16_t *)heap_mem->ptr;
86
+ buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32)) / 2;
87
+
88
+ status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
89
+ RG_ASSERT(status == napi_ok);
90
+
91
+ len++;
92
+
93
+ if (RG_LIKELY(len < (size_t)buf.len)) {
94
+ heap_mem->ptr += (Size)len * 2;
95
+ heap_mem->len -= (Size)len * 2;
96
+ } else {
97
+ status = napi_get_value_string_utf16(env, value, nullptr, 0, &len);
98
+ RG_ASSERT(status == napi_ok);
99
+
100
+ len++;
101
+
102
+ buf.ptr = (char16_t *)Allocator::Allocate(&big_alloc, (Size)len * 2);
103
+ buf.len = (Size)len;
104
+
105
+ status = napi_get_value_string_utf16(env, value, buf.ptr, (size_t)buf.len, &len);
106
+ RG_ASSERT(status == napi_ok);
107
+ }
108
+
109
+ return buf.ptr;
110
+ }
111
+
112
+ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest)
113
+ {
114
+ Napi::Env env = obj.Env();
115
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
116
+
117
+ RG_ASSERT(IsObject(obj));
118
+ RG_ASSERT(type->primitive == PrimitiveKind::Record);
119
+
120
+ for (const RecordMember &member: type->members) {
121
+ Napi::Value value = obj.Get(member.name);
122
+
123
+ if (RG_UNLIKELY(value.IsUndefined())) {
124
+ ThrowError<Napi::TypeError>(env, "Missing expected object property '%1'", member.name);
125
+ return false;
126
+ }
127
+
128
+ dest = AlignUp(dest, member.align);
129
+
130
+ switch (member.type->primitive) {
131
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
132
+
133
+ case PrimitiveKind::Bool: {
134
+ if (RG_UNLIKELY(!value.IsBoolean())) {
135
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected boolean", GetValueType(instance, value), member.name);
136
+ return false;
137
+ }
138
+
139
+ bool b = value.As<Napi::Boolean>();
140
+ *(bool *)dest = b;
141
+ } break;
142
+
143
+ case PrimitiveKind::Int8:
144
+ case PrimitiveKind::UInt8:
145
+ case PrimitiveKind::Int16:
146
+ case PrimitiveKind::UInt16:
147
+ case PrimitiveKind::Int32:
148
+ case PrimitiveKind::UInt32:
149
+ case PrimitiveKind::Int64:
150
+ case PrimitiveKind::UInt64: {
151
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
152
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
153
+ return false;
154
+ }
155
+
156
+ int64_t v = CopyNumber<int64_t>(value);
157
+ memcpy(dest, &v, member.type->size); // Little Endian
158
+ } break;
159
+ case PrimitiveKind::Float32: {
160
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
161
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
162
+ return false;
163
+ }
164
+
165
+ float f = CopyNumber<float>(value);
166
+ memcpy(dest, &f, 4);
167
+ } break;
168
+ case PrimitiveKind::Float64: {
169
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
170
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
171
+ return false;
172
+ }
173
+
174
+ double d = CopyNumber<double>(value);
175
+ memcpy(dest, &d, 8);
176
+ } break;
177
+ case PrimitiveKind::String: {
178
+ if (RG_UNLIKELY(!value.IsString())) {
179
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected string", GetValueType(instance, value), member.name);
180
+ return false;
181
+ }
182
+
183
+ const char *str = PushString(value);
184
+ if (RG_UNLIKELY(!str))
185
+ return false;
186
+ *(const char **)dest = str;
187
+ } break;
188
+ case PrimitiveKind::String16: {
189
+ if (RG_UNLIKELY(!value.IsString())) {
190
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected string", GetValueType(instance, value), member.name);
191
+ return false;
192
+ }
193
+
194
+ const char16_t *str16 = PushString16(value);
195
+ if (RG_UNLIKELY(!str16))
196
+ return false;
197
+ *(const char16_t **)dest = str16;
198
+ } break;
199
+ case PrimitiveKind::Pointer: {
200
+ if (RG_UNLIKELY(!CheckValueTag(instance, value, member.type))) {
201
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for member '%2', expected %3", GetValueType(instance, value), member.name, member.type->name);
202
+ return false;
203
+ }
204
+
205
+ Napi::External external = value.As<Napi::External<void>>();
206
+ void *ptr = external.Data();
207
+ *(void **)dest = ptr;
208
+ } break;
209
+
210
+ case PrimitiveKind::Record: {
211
+ if (RG_UNLIKELY(!IsObject(value))) {
212
+ ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected object", GetValueType(instance, value), member.name);
213
+ return false;
214
+ }
215
+
216
+ Napi::Object obj = value.As<Napi::Object>();
217
+ if (!PushObject(obj, member.type, dest))
218
+ return false;
219
+ } break;
220
+ }
221
+
222
+ dest += member.type->size;
223
+ }
224
+
225
+ return true;
226
+ }
227
+
228
+ }
package/src/call.hh CHANGED
@@ -14,15 +14,107 @@
14
14
  #pragma once
15
15
 
16
16
  #include "vendor/libcc/libcc.hh"
17
+ #include "ffi.hh"
18
+ #include "util.hh"
17
19
 
18
20
  #include <napi.h>
19
21
 
20
22
  namespace RG {
21
23
 
22
- struct InstanceData;
23
- struct FunctionInfo;
24
-
25
24
  bool AnalyseFunction(InstanceData *instance, FunctionInfo *func);
26
- Napi::Value TranslateCall(InstanceData *instance, const FunctionInfo *func, const Napi::CallbackInfo &info);
25
+
26
+ class CallData {
27
+ Napi::Env env;
28
+ InstanceData *instance;
29
+ const FunctionInfo *func;
30
+
31
+ Span<uint8_t> *stack_mem;
32
+ Span<uint8_t> *heap_mem;
33
+ BlockAllocator big_alloc;
34
+
35
+ Span<uint8_t> old_stack_mem;
36
+ Span<uint8_t> old_heap_mem;
37
+
38
+ public:
39
+ CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func);
40
+ ~CallData();
41
+
42
+ Span<uint8_t> GetStack() const
43
+ {
44
+ uint8_t *sp = stack_mem->end();
45
+ Size len = old_stack_mem.end() - sp;
46
+
47
+ return MakeSpan(sp, len);
48
+ }
49
+ uint8_t *GetSP() const { return stack_mem->end(); };
50
+
51
+ Span<uint8_t> GetHeap() const
52
+ {
53
+ uint8_t *ptr = old_heap_mem.ptr;
54
+ Size len = heap_mem->ptr - ptr;
55
+
56
+ return MakeSpan(ptr, len);
57
+ }
58
+
59
+ template <typename T = void>
60
+ bool AllocStack(Size size, Size align, T **out_ptr = nullptr);
61
+ template <typename T = void>
62
+ bool AllocHeap(Size size, Size align, T **out_ptr = nullptr);
63
+
64
+ const char *PushString(const Napi::Value &value);
65
+ const char16_t *PushString16(const Napi::Value &value);
66
+ bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest);
67
+
68
+ void DumpDebug() const;
69
+
70
+ Napi::Value Execute(const Napi::CallbackInfo &info);
71
+ };
72
+
73
+ template <typename T>
74
+ bool CallData::AllocStack(Size size, Size align, T **out_ptr)
75
+ {
76
+ uint8_t *ptr = AlignDown(stack_mem->end() - size, align);
77
+ Size delta = stack_mem->end() - ptr;
78
+
79
+ if (RG_UNLIKELY(stack_mem->len < delta)) {
80
+ ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
81
+ return false;
82
+ }
83
+
84
+ if (instance->debug) {
85
+ memset(ptr, 0, delta);
86
+ }
87
+
88
+ stack_mem->len -= delta;
89
+
90
+ if (out_ptr) {
91
+ *out_ptr = (T *)ptr;
92
+ }
93
+ return true;
94
+ }
95
+
96
+ template <typename T>
97
+ bool CallData::AllocHeap(Size size, Size align, T **out_ptr)
98
+ {
99
+ uint8_t *ptr = AlignUp(heap_mem->ptr, align);
100
+ Size delta = size + (ptr - heap_mem->ptr);
101
+
102
+ if (RG_UNLIKELY(delta > heap_mem->len)) {
103
+ ThrowError<Napi::Error>(env, "FFI call is taking up too much memory");
104
+ return false;
105
+ }
106
+
107
+ if (instance->debug) {
108
+ memset(heap_mem->ptr, 0, (size_t)delta);
109
+ }
110
+
111
+ heap_mem->ptr += delta;
112
+ heap_mem->len -= delta;
113
+
114
+ if (out_ptr) {
115
+ *out_ptr = (T *)ptr;
116
+ }
117
+ return true;
118
+ }
27
119
 
28
120
  }
package/src/ffi.cc CHANGED
@@ -247,7 +247,8 @@ static Napi::Value TranslateNormalCall(const Napi::CallbackInfo &info)
247
247
  InstanceData *instance = env.GetInstanceData<InstanceData>();
248
248
  FunctionInfo *func = (FunctionInfo *)info.Data();
249
249
 
250
- return TranslateCall(instance, func, info);
250
+ CallData call(env, instance, func);
251
+ return call.Execute(info);
251
252
  }
252
253
 
253
254
  static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
@@ -299,7 +300,8 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
299
300
  if (!AnalyseFunction(instance, &func))
300
301
  return env.Null();
301
302
 
302
- return TranslateCall(instance, &func, info);
303
+ CallData call(env, instance, &func);
304
+ return call.Execute(info);
303
305
  }
304
306
 
305
307
  static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value ret,
@@ -319,7 +321,7 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
319
321
  }
320
322
  #endif
321
323
 
322
- func->name = DuplicateString(std::string(name).c_str(), &instance->str_alloc).ptr;
324
+ func->name = DuplicateString(name.ToString().Utf8Value().c_str(), &instance->str_alloc).ptr;
323
325
 
324
326
  func->ret.type = ResolveType(instance, ret);
325
327
  if (!func->ret.type)
@@ -573,6 +575,8 @@ static Napi::Object InitBaseTypes(Napi::Env env)
573
575
  RegisterPrimitiveType(instance, "char", PrimitiveKind::Int8, 1, 1);
574
576
  RegisterPrimitiveType(instance, "uchar", PrimitiveKind::UInt8, 1, 1);
575
577
  RegisterPrimitiveType(instance, "unsigned char", PrimitiveKind::UInt8, 1, 1);
578
+ RegisterPrimitiveType(instance, "char16", PrimitiveKind::Int16, 2, 2);
579
+ RegisterPrimitiveType(instance, "char16_t", PrimitiveKind::Int16, 2, 2);
576
580
  RegisterPrimitiveType(instance, "int16", PrimitiveKind::Int16, 2, 2);
577
581
  RegisterPrimitiveType(instance, "int16_t", PrimitiveKind::Int16, 2, 2);
578
582
  RegisterPrimitiveType(instance, "uint16", PrimitiveKind::UInt16, 2, 2);
@@ -603,6 +607,7 @@ static Napi::Object InitBaseTypes(Napi::Env env)
603
607
  RegisterPrimitiveType(instance, "float", PrimitiveKind::Float32, 4, alignof(float));
604
608
  RegisterPrimitiveType(instance, "double", PrimitiveKind::Float64, 8, alignof(double));
605
609
  RegisterPrimitiveType(instance, "string", PrimitiveKind::String, RG_SIZE(void *), alignof(void *));
610
+ RegisterPrimitiveType(instance, "string16", PrimitiveKind::String16, RG_SIZE(void *), alignof(void *));
606
611
 
607
612
  Napi::Object types = Napi::Object::New(env);
608
613
  for (TypeInfo &type: instance->types) {
package/src/ffi.hh CHANGED
@@ -39,6 +39,7 @@ enum class PrimitiveKind {
39
39
  Float32,
40
40
  Float64,
41
41
  String,
42
+ String16,
42
43
 
43
44
  Pointer,
44
45
 
@@ -59,6 +60,7 @@ static const char *const PrimitiveKindNames[] = {
59
60
  "Float32",
60
61
  "Float64",
61
62
  "String",
63
+ "String16",
62
64
 
63
65
  "Pointer",
64
66