koffi 0.9.48 → 1.0.2
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/CMakeLists.txt +12 -11
- package/README.md +152 -91
- package/build/qemu/1.0.2/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.2/koffi_win32_x64.tar.gz +0 -0
- package/package.json +8 -4
- package/qemu/qemu.js +794 -0
- package/qemu/registry/machines.json +415 -0
- package/qemu/registry/sha256sum.txt +45 -0
- package/src/{call_arm32.cc → abi_arm32.cc} +76 -50
- package/src/{call_arm32_fwd.S → abi_arm32_fwd.S} +0 -0
- package/src/{call_arm64.cc → abi_arm64.cc} +79 -51
- package/src/{call_arm64_fwd.S → abi_arm64_fwd.S} +0 -0
- package/src/{call_x64_sysv.cc → abi_x64_sysv.cc} +77 -49
- package/src/{call_x64_sysv_fwd.S → abi_x64_sysv_fwd.S} +0 -0
- package/src/{call_x64_win.cc → abi_x64_win.cc} +71 -47
- package/src/{call_x64_win_fwd.asm → abi_x64_win_fwd.asm} +0 -0
- package/src/{call_x86.cc → abi_x86.cc} +74 -56
- package/src/{call_x86_fwd.S → abi_x86_fwd.S} +0 -0
- package/src/{call_x86_fwd.asm → abi_x86_fwd.asm} +0 -0
- package/src/call.cc +228 -0
- package/src/call.hh +96 -4
- package/src/ffi.cc +8 -3
- package/src/ffi.hh +2 -0
- package/src/util.cc +11 -157
- package/src/util.hh +0 -91
- package/test/CMakeLists.txt +1 -0
- package/test/misc.c +289 -0
- package/test/misc.def +3 -0
- package/test/misc.js +180 -0
- package/test/raylib.js +165 -0
- package/test/sqlite.js +104 -0
- package/build/qemu/0.9.48/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/0.9.48/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/0.9.48/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
|
|
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(!
|
|
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(!
|
|
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 =
|
|
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(!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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,
|
|
253
|
-
: ForwardCall ## Suffix(func->func,
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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