koffi 0.9.28 → 0.9.31
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/package.json +1 -1
- package/src/call_arm32.cc +13 -8
- package/src/call_arm32_fwd.S +12 -12
- package/src/call_x86.cc +45 -18
- package/src/call_x86_fwd.S +1 -1
- package/src/call_x86_fwd.asm +1 -1
- package/src/ffi.cc +42 -17
- package/src/ffi.hh +7 -6
- package/src/util.cc +23 -25
- package/src/util.hh +1 -0
- package/test/test.js +1 -1
- package/test/tests/misc.c +98 -3
- package/test/tests/misc.js +96 -19
package/package.json
CHANGED
package/src/call_arm32.cc
CHANGED
|
@@ -63,8 +63,9 @@ Napi::Function::Callback AnalyseFunction(InstanceData *, FunctionInfo *func)
|
|
|
63
63
|
if (IsHFA(func->ret.type)) {
|
|
64
64
|
func->ret.vec_count = func->ret.type->members.len *
|
|
65
65
|
(func->ret.type->members[0].type->size / 4);
|
|
66
|
-
} else if (func->ret.type->primitive != PrimitiveKind::Record
|
|
67
|
-
|
|
66
|
+
} else if (func->ret.type->primitive != PrimitiveKind::Record ||
|
|
67
|
+
func->ret.type->size <= 4) {
|
|
68
|
+
func->ret.gpr_count = (func->ret.type->size > 4) ? 2 : 1;
|
|
68
69
|
} else {
|
|
69
70
|
func->ret.use_memory = true;
|
|
70
71
|
}
|
|
@@ -230,19 +231,21 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
230
231
|
uint32_t *gpr_ptr = nullptr;
|
|
231
232
|
uint32_t *vec_ptr = nullptr;
|
|
232
233
|
|
|
233
|
-
//
|
|
234
|
+
// Unlike other call conventions, here we put the general-purpose
|
|
235
|
+
// registers just before the stack (so behind the vector ones).
|
|
236
|
+
// In the armv7hf calling convention, some arguments can end up
|
|
237
|
+
// partially in GPR, partially in the stack.
|
|
234
238
|
if (RG_UNLIKELY(!call.AllocStack(func->args_size, 16, &args_ptr)))
|
|
235
239
|
return env.Null();
|
|
236
|
-
if (RG_UNLIKELY(!call.AllocStack(8 * 8, 8, &vec_ptr)))
|
|
237
|
-
return env.Null();
|
|
238
240
|
if (RG_UNLIKELY(!call.AllocStack(4 * 4, 8, &gpr_ptr)))
|
|
239
241
|
return env.Null();
|
|
242
|
+
if (RG_UNLIKELY(!call.AllocStack(8 * 8, 8, &vec_ptr)))
|
|
243
|
+
return env.Null();
|
|
240
244
|
if (func->ret.use_memory) {
|
|
241
245
|
if (RG_UNLIKELY(!call.AllocHeap(func->ret.type->size, 16, &return_ptr)))
|
|
242
246
|
return env.Null();
|
|
243
247
|
*(uint8_t **)(gpr_ptr++) = return_ptr;
|
|
244
248
|
}
|
|
245
|
-
RG_ASSERT((uint8_t *)gpr_ptr + 16 == args_ptr);
|
|
246
249
|
|
|
247
250
|
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
248
251
|
|
|
@@ -455,6 +458,8 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
455
458
|
|
|
456
459
|
case PrimitiveKind::Record: {
|
|
457
460
|
if (func->ret.gpr_count) {
|
|
461
|
+
RG_ASSERT(func->ret.gpr_count <= 1);
|
|
462
|
+
|
|
458
463
|
uint64_t ret = PERFORM_CALL(GG);
|
|
459
464
|
uint32_t r0 = (uint32_t)ret;
|
|
460
465
|
|
|
@@ -469,8 +474,8 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
469
474
|
RG_ASSERT(return_ptr);
|
|
470
475
|
|
|
471
476
|
uint64_t ret = PERFORM_CALL(GG);
|
|
472
|
-
|
|
473
|
-
RG_ASSERT(r0 == (
|
|
477
|
+
uint32_t r0 = (uint32_t)ret;
|
|
478
|
+
RG_ASSERT(r0 == (uint32_t)return_ptr);
|
|
474
479
|
|
|
475
480
|
Napi::Object obj = PopObject(env, return_ptr, func->ret.type);
|
|
476
481
|
return obj;
|
package/src/call_arm32_fwd.S
CHANGED
|
@@ -56,22 +56,22 @@
|
|
|
56
56
|
|
|
57
57
|
// Prepare general purpose argument registers from array passed by caller.
|
|
58
58
|
.macro forward_int
|
|
59
|
-
ldr r3, [r1,
|
|
60
|
-
ldr r2, [r1,
|
|
61
|
-
ldr r0, [r1,
|
|
62
|
-
ldr r1, [r1,
|
|
59
|
+
ldr r3, [r1, 76]
|
|
60
|
+
ldr r2, [r1, 72]
|
|
61
|
+
ldr r0, [r1, 64]
|
|
62
|
+
ldr r1, [r1, 68]
|
|
63
63
|
.endm
|
|
64
64
|
|
|
65
65
|
// Prepare vector argument registers from array passed by caller.
|
|
66
66
|
.macro forward_vec
|
|
67
|
-
vldr d7, [r1,
|
|
68
|
-
vldr d6, [r1,
|
|
69
|
-
vldr d5, [r1,
|
|
70
|
-
vldr d4, [r1,
|
|
71
|
-
vldr d3, [r1,
|
|
72
|
-
vldr d2, [r1,
|
|
73
|
-
vldr d1, [r1,
|
|
74
|
-
vldr d0, [r1,
|
|
67
|
+
vldr d7, [r1, 56]
|
|
68
|
+
vldr d6, [r1, 48]
|
|
69
|
+
vldr d5, [r1, 40]
|
|
70
|
+
vldr d4, [r1, 32]
|
|
71
|
+
vldr d3, [r1, 24]
|
|
72
|
+
vldr d2, [r1, 16]
|
|
73
|
+
vldr d1, [r1, 8]
|
|
74
|
+
vldr d0, [r1, 0]
|
|
75
75
|
.endm
|
|
76
76
|
|
|
77
77
|
ForwardCallGG:
|
package/src/call_x86.cc
CHANGED
|
@@ -31,21 +31,37 @@ extern "C" double ForwardCallRD(const void *func, uint8_t *sp);
|
|
|
31
31
|
|
|
32
32
|
static Napi::Value TranslateCall(const Napi::CallbackInfo &info);
|
|
33
33
|
|
|
34
|
+
static inline bool IsRegular(Size size)
|
|
35
|
+
{
|
|
36
|
+
bool regular = (size <= 8 && !(size & (size - 1)));
|
|
37
|
+
return regular;
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
Napi::Function::Callback AnalyseFunction(InstanceData *instance, FunctionInfo *func)
|
|
35
41
|
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
int fast = (func->convention == CallConvention::Fastcall) ? 2 : 0;
|
|
43
|
+
|
|
44
|
+
if (func->ret.type->primitive != PrimitiveKind::Record) {
|
|
39
45
|
func->ret.trivial = true;
|
|
40
46
|
#ifdef _WIN32
|
|
41
|
-
} else
|
|
42
|
-
|
|
43
|
-
func->ret.trivial = true;
|
|
47
|
+
} else {
|
|
48
|
+
func->ret.trivial = IsRegular(func->ret.type->size);
|
|
44
49
|
#endif
|
|
45
50
|
}
|
|
51
|
+
#ifndef _WIN32
|
|
52
|
+
if (fast && !func->ret.trivial) {
|
|
53
|
+
func->ret.fast = true;
|
|
54
|
+
fast--;
|
|
55
|
+
}
|
|
56
|
+
#endif
|
|
46
57
|
|
|
47
58
|
Size params_size = 0;
|
|
48
|
-
for (
|
|
59
|
+
for (ParameterInfo ¶m: func->parameters) {
|
|
60
|
+
if (fast && param.type->size <= 4) {
|
|
61
|
+
param.fast = true;
|
|
62
|
+
fast--;
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
params_size += std::max((int16_t)4, param.type->size);
|
|
50
66
|
}
|
|
51
67
|
func->args_size = params_size + 4 * !func->ret.trivial;
|
|
@@ -57,7 +73,7 @@ Napi::Function::Callback AnalyseFunction(InstanceData *instance, FunctionInfo *f
|
|
|
57
73
|
} break;
|
|
58
74
|
case CallConvention::Fastcall: {
|
|
59
75
|
func->decorated_name = Fmt(&instance->str_alloc, "@%1@%2", func->name, params_size).ptr;
|
|
60
|
-
func->args_size
|
|
76
|
+
func->args_size += 16;
|
|
61
77
|
} break;
|
|
62
78
|
}
|
|
63
79
|
|
|
@@ -80,14 +96,19 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
80
96
|
|
|
81
97
|
uint8_t *return_ptr = nullptr;
|
|
82
98
|
uint32_t *args_ptr = nullptr;
|
|
99
|
+
uint32_t *fast_ptr = nullptr;
|
|
83
100
|
|
|
84
101
|
// Pass return value in register or through memory
|
|
85
102
|
if (RG_UNLIKELY(!call.AllocStack(func->args_size, 16, &args_ptr)))
|
|
86
103
|
return env.Null();
|
|
104
|
+
if (func->convention == CallConvention::Fastcall) {
|
|
105
|
+
fast_ptr = args_ptr;
|
|
106
|
+
args_ptr += 4;
|
|
107
|
+
}
|
|
87
108
|
if (!func->ret.trivial) {
|
|
88
109
|
if (RG_UNLIKELY(!call.AllocHeap(func->ret.type->size, 16, &return_ptr)))
|
|
89
110
|
return env.Null();
|
|
90
|
-
*(args_ptr++) = (uint32_t)return_ptr;
|
|
111
|
+
*((func->ret.fast ? fast_ptr : args_ptr)++) = (uint32_t)return_ptr;
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
@@ -109,7 +130,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
bool b = value.As<Napi::Boolean>();
|
|
112
|
-
*(bool *)(args_ptr++) = b;
|
|
133
|
+
*(bool *)((param.fast ? fast_ptr : args_ptr)++) = b;
|
|
113
134
|
} break;
|
|
114
135
|
case PrimitiveKind::Int8:
|
|
115
136
|
case PrimitiveKind::UInt8:
|
|
@@ -123,7 +144,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
int32_t v = CopyNumber<int32_t>(value);
|
|
126
|
-
*(args_ptr++) = (uint32_t)v;
|
|
147
|
+
*((param.fast ? fast_ptr : args_ptr)++) = (uint32_t)v;
|
|
127
148
|
} break;
|
|
128
149
|
case PrimitiveKind::Int64:
|
|
129
150
|
case PrimitiveKind::UInt64: {
|
|
@@ -143,7 +164,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
143
164
|
}
|
|
144
165
|
|
|
145
166
|
float f = CopyNumber<float>(value);
|
|
146
|
-
*(float *)(args_ptr++) = f;
|
|
167
|
+
*(float *)((param.fast ? fast_ptr : args_ptr)++) = f;
|
|
147
168
|
} break;
|
|
148
169
|
case PrimitiveKind::Float64: {
|
|
149
170
|
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
@@ -168,7 +189,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
168
189
|
return env.Null();
|
|
169
190
|
}
|
|
170
191
|
|
|
171
|
-
*(const char **)(args_ptr++) = str;
|
|
192
|
+
*(const char **)((param.fast ? fast_ptr : args_ptr)++) = str;
|
|
172
193
|
} break;
|
|
173
194
|
case PrimitiveKind::Pointer: {
|
|
174
195
|
uint8_t *ptr;
|
|
@@ -194,7 +215,7 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
194
215
|
return env.Null();
|
|
195
216
|
}
|
|
196
217
|
|
|
197
|
-
*(uint8_t **)(args_ptr++) = ptr;
|
|
218
|
+
*(uint8_t **)((param.fast ? fast_ptr : args_ptr)++) = ptr;
|
|
198
219
|
} break;
|
|
199
220
|
|
|
200
221
|
case PrimitiveKind::Record: {
|
|
@@ -205,10 +226,16 @@ static Napi::Value TranslateCall(const Napi::CallbackInfo &info)
|
|
|
205
226
|
|
|
206
227
|
Napi::Object obj = value.As<Napi::Object>();
|
|
207
228
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
if (param.fast) {
|
|
230
|
+
uint8_t *ptr = (uint8_t *)(fast_ptr++);
|
|
231
|
+
if (!call.PushObject(obj, param.type, ptr))
|
|
232
|
+
return env.Null();
|
|
233
|
+
} else {
|
|
234
|
+
uint8_t *ptr = (uint8_t *)AlignUp(args_ptr, param.type->align);
|
|
235
|
+
if (!call.PushObject(obj, param.type, ptr))
|
|
236
|
+
return env.Null();
|
|
237
|
+
args_ptr = (uint32_t *)AlignUp(ptr + param.type->size, 4);
|
|
238
|
+
}
|
|
212
239
|
} break;
|
|
213
240
|
}
|
|
214
241
|
}
|
package/src/call_x86_fwd.S
CHANGED
package/src/call_x86_fwd.asm
CHANGED
package/src/ffi.cc
CHANGED
|
@@ -75,20 +75,23 @@ static const TypeInfo *ResolveType(const InstanceData *instance, Napi::Value val
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
78
|
+
static Napi::Value CreateStructType(const Napi::CallbackInfo &info, bool pad)
|
|
79
79
|
{
|
|
80
80
|
Napi::Env env = info.Env();
|
|
81
81
|
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
82
82
|
|
|
83
|
-
if (info.Length() <
|
|
84
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
83
|
+
if (info.Length() < 1) {
|
|
84
|
+
ThrowError<Napi::TypeError>(env, "Expected 1 or 2 arguments, got %1", info.Length());
|
|
85
85
|
return env.Null();
|
|
86
86
|
}
|
|
87
|
-
|
|
87
|
+
|
|
88
|
+
bool named = info.Length() > 1;
|
|
89
|
+
|
|
90
|
+
if (named && !info[0].IsString()) {
|
|
88
91
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for name, expected string", GetValueType(instance, info[0]));
|
|
89
92
|
return env.Null();
|
|
90
93
|
}
|
|
91
|
-
if (!IsObject(info[
|
|
94
|
+
if (!IsObject(info[named])) {
|
|
92
95
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for members, expected object", GetValueType(instance, info[1]));
|
|
93
96
|
return env.Null();
|
|
94
97
|
}
|
|
@@ -96,8 +99,8 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
96
99
|
TypeInfo *type = instance->types.AppendDefault();
|
|
97
100
|
RG_DEFER_N(err_guard) { instance->types.RemoveLast(1); };
|
|
98
101
|
|
|
99
|
-
std::string name = info[0].As<Napi::String>();
|
|
100
|
-
Napi::Object obj = info[
|
|
102
|
+
std::string name = named ? info[0].As<Napi::String>() : std::string("<anonymous>");
|
|
103
|
+
Napi::Object obj = info[named].As<Napi::Object>();
|
|
101
104
|
Napi::Array keys = obj.GetPropertyNames();
|
|
102
105
|
|
|
103
106
|
type->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
|
|
@@ -116,8 +119,10 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
116
119
|
if (!member.type)
|
|
117
120
|
return env.Null();
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
member.align = pad ? member.type->align : 1;
|
|
123
|
+
|
|
124
|
+
type->size = (int16_t)(AlignLen(type->size, member.align) + member.type->size);
|
|
125
|
+
type->align = std::max(type->align, member.align);
|
|
121
126
|
|
|
122
127
|
type->members.Append(member);
|
|
123
128
|
}
|
|
@@ -125,7 +130,7 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
125
130
|
type->size = (int16_t)AlignLen(type->size, type->align);
|
|
126
131
|
|
|
127
132
|
// If the insert succeeds, we cannot fail anymore
|
|
128
|
-
if (!instance->types_map.TrySet(type).second) {
|
|
133
|
+
if (named && !instance->types_map.TrySet(type).second) {
|
|
129
134
|
ThrowError<Napi::Error>(env, "Duplicate type name '%1'", type->name);
|
|
130
135
|
return env.Null();
|
|
131
136
|
}
|
|
@@ -137,6 +142,16 @@ static Napi::Value CreateStructType(const Napi::CallbackInfo &info)
|
|
|
137
142
|
return external;
|
|
138
143
|
}
|
|
139
144
|
|
|
145
|
+
static Napi::Value CreatePaddedStructType(const Napi::CallbackInfo &info)
|
|
146
|
+
{
|
|
147
|
+
return CreateStructType(info, true);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
static Napi::Value CreatePackedStructType(const Napi::CallbackInfo &info)
|
|
151
|
+
{
|
|
152
|
+
return CreateStructType(info, false);
|
|
153
|
+
}
|
|
154
|
+
|
|
140
155
|
static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
|
|
141
156
|
{
|
|
142
157
|
Napi::Env env = info.Env();
|
|
@@ -159,7 +174,7 @@ static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
|
|
|
159
174
|
type->name = DuplicateString(name.c_str(), &instance->str_alloc).ptr;
|
|
160
175
|
|
|
161
176
|
type->primitive = PrimitiveKind::Record;
|
|
162
|
-
type->align =
|
|
177
|
+
type->align = alignof(void *);
|
|
163
178
|
type->size = RG_SIZE(void *);
|
|
164
179
|
|
|
165
180
|
// Add single handle member
|
|
@@ -169,6 +184,7 @@ static Napi::Value CreateHandleType(const Napi::CallbackInfo &info)
|
|
|
169
184
|
member.name = "value";
|
|
170
185
|
member.type = instance->types_map.FindValue("void *", nullptr);
|
|
171
186
|
RG_ASSERT(member.type);
|
|
187
|
+
member.align = type->align;
|
|
172
188
|
|
|
173
189
|
type->members.Append(member);
|
|
174
190
|
}
|
|
@@ -552,19 +568,28 @@ FunctionInfo::~FunctionInfo()
|
|
|
552
568
|
}
|
|
553
569
|
}
|
|
554
570
|
|
|
555
|
-
|
|
571
|
+
static Span<uint8_t> AllocateAndAlign16(Allocator *alloc, Size size)
|
|
556
572
|
{
|
|
557
|
-
|
|
558
|
-
|
|
573
|
+
RG_ASSERT(AlignLen(size, 16) == size);
|
|
574
|
+
|
|
575
|
+
uint8_t *ptr = (uint8_t *)Allocator::Allocate(alloc, size);
|
|
576
|
+
uint8_t *aligned = AlignUp(ptr, 16);
|
|
577
|
+
Size delta = AlignLen(aligned - ptr, 16);
|
|
559
578
|
|
|
560
|
-
|
|
561
|
-
|
|
579
|
+
return MakeSpan(aligned, size - delta);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
InstanceData::InstanceData()
|
|
583
|
+
{
|
|
584
|
+
stack_mem = AllocateAndAlign16(&mem_alloc, Mebibytes(2));
|
|
585
|
+
heap_mem = AllocateAndAlign16(&mem_alloc, Mebibytes(4));
|
|
562
586
|
}
|
|
563
587
|
|
|
564
588
|
template <typename Func>
|
|
565
589
|
static void SetExports(Napi::Env env, Func func)
|
|
566
590
|
{
|
|
567
|
-
func("struct", Napi::Function::New(env,
|
|
591
|
+
func("struct", Napi::Function::New(env, CreatePaddedStructType));
|
|
592
|
+
func("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
568
593
|
func("handle", Napi::Function::New(env, CreateHandleType));
|
|
569
594
|
func("pointer", Napi::Function::New(env, CreatePointerType));
|
|
570
595
|
func("load", Napi::Function::New(env, LoadSharedLibrary));
|
package/src/ffi.hh
CHANGED
|
@@ -63,12 +63,6 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
63
63
|
"Record"
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
static inline bool IsIntegral(PrimitiveKind primitive)
|
|
67
|
-
{
|
|
68
|
-
bool integral = ((int)primitive <= (int)PrimitiveKind::Pointer);
|
|
69
|
-
return integral;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
66
|
struct TypeInfo;
|
|
73
67
|
struct RecordMember;
|
|
74
68
|
|
|
@@ -89,6 +83,7 @@ struct TypeInfo {
|
|
|
89
83
|
struct RecordMember {
|
|
90
84
|
const char *name;
|
|
91
85
|
const TypeInfo *type;
|
|
86
|
+
int16_t align;
|
|
92
87
|
};
|
|
93
88
|
|
|
94
89
|
struct LibraryHolder {
|
|
@@ -107,6 +102,11 @@ enum class CallConvention {
|
|
|
107
102
|
Stdcall,
|
|
108
103
|
Fastcall
|
|
109
104
|
};
|
|
105
|
+
static const char *const CallConventionNames[] = {
|
|
106
|
+
"Default",
|
|
107
|
+
"Stdcall",
|
|
108
|
+
"Fastcall"
|
|
109
|
+
};
|
|
110
110
|
|
|
111
111
|
struct ParameterInfo {
|
|
112
112
|
const TypeInfo *type;
|
|
@@ -127,6 +127,7 @@ struct ParameterInfo {
|
|
|
127
127
|
int8_t vec_count;
|
|
128
128
|
#elif defined(__i386__) || defined(_M_IX86)
|
|
129
129
|
bool trivial; // Only matters for return value
|
|
130
|
+
bool fast;
|
|
130
131
|
#endif
|
|
131
132
|
};
|
|
132
133
|
|
package/src/util.cc
CHANGED
|
@@ -81,36 +81,34 @@ const char *CallData::PushString(const Napi::Value &value)
|
|
|
81
81
|
{
|
|
82
82
|
RG_ASSERT(value.IsString());
|
|
83
83
|
|
|
84
|
-
const Size SmallSize = 32;
|
|
85
|
-
|
|
86
84
|
Napi::Env env = value.Env();
|
|
87
|
-
napi_status status;
|
|
88
85
|
|
|
89
86
|
Span<char> buf;
|
|
90
87
|
size_t len = 0;
|
|
88
|
+
napi_status status;
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
buf.len =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
ThrowError<Napi::Error>(env, "Failed to convert string to UTF-8");
|
|
98
|
-
return nullptr;
|
|
99
|
-
}
|
|
90
|
+
buf.ptr = (char *)heap_mem->ptr;
|
|
91
|
+
buf.len = std::max((Size)0, heap_mem->len - Kibibytes(32));
|
|
92
|
+
|
|
93
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
94
|
+
RG_ASSERT(status == napi_ok);
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
len++;
|
|
97
|
+
|
|
98
|
+
if (RG_LIKELY(len < (size_t)buf.len)) {
|
|
99
|
+
heap_mem->ptr += (Size)len;
|
|
100
|
+
heap_mem->len -= (Size)len;
|
|
101
|
+
} else {
|
|
103
102
|
status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
|
|
104
103
|
RG_ASSERT(status == napi_ok);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
104
|
+
|
|
105
|
+
len++;
|
|
106
|
+
|
|
107
|
+
buf.ptr = (char *)Allocator::Allocate(&big_alloc, (Size)len);
|
|
108
|
+
buf.len = (Size)len;
|
|
109
|
+
|
|
110
|
+
status = napi_get_value_string_utf8(env, value, buf.ptr, (size_t)buf.len, &len);
|
|
111
|
+
RG_ASSERT(status == napi_ok);
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
return buf.ptr;
|
|
@@ -132,7 +130,7 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
132
130
|
return false;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
dest = AlignUp(dest, member.
|
|
133
|
+
dest = AlignUp(dest, member.align);
|
|
136
134
|
|
|
137
135
|
switch (member.type->primitive) {
|
|
138
136
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
@@ -229,7 +227,7 @@ void PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type)
|
|
|
229
227
|
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
230
228
|
|
|
231
229
|
for (const RecordMember &member: type->members) {
|
|
232
|
-
ptr = AlignUp(ptr, member.
|
|
230
|
+
ptr = AlignUp(ptr, member.align);
|
|
233
231
|
|
|
234
232
|
switch (member.type->primitive) {
|
|
235
233
|
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
@@ -329,7 +327,7 @@ static void DumpMemory(const char *type, Span<const uint8_t> bytes)
|
|
|
329
327
|
|
|
330
328
|
void CallData::DumpDebug() const
|
|
331
329
|
{
|
|
332
|
-
PrintLn(stderr, "%!..+---- %1 ----%!0", func->name);
|
|
330
|
+
PrintLn(stderr, "%!..+---- %1 (%2) ----%!0", func->name, CallConventionNames[(int)func->convention]);
|
|
333
331
|
|
|
334
332
|
if (func->parameters.len) {
|
|
335
333
|
PrintLn(stderr, "Parameters:");
|
package/src/util.hh
CHANGED
package/test/test.js
CHANGED
package/test/tests/misc.c
CHANGED
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
// You should have received a copy of the GNU Affero General Public License
|
|
12
12
|
// along with this program. If not, see https://www.gnu.org/licenses/.
|
|
13
13
|
|
|
14
|
+
#include <stdlib.h>
|
|
14
15
|
#include <stdio.h>
|
|
15
16
|
#include <inttypes.h>
|
|
17
|
+
#include <string.h>
|
|
16
18
|
|
|
17
19
|
#ifdef _WIN32
|
|
18
20
|
#define EXPORT __declspec(dllexport)
|
|
@@ -32,6 +34,13 @@
|
|
|
32
34
|
#define STDCALL
|
|
33
35
|
#endif
|
|
34
36
|
|
|
37
|
+
typedef struct Pack1 {
|
|
38
|
+
int a;
|
|
39
|
+
} Pack1;
|
|
40
|
+
typedef struct Pack2 {
|
|
41
|
+
int a;
|
|
42
|
+
int b;
|
|
43
|
+
} Pack2;
|
|
35
44
|
typedef struct Pack3 {
|
|
36
45
|
int a;
|
|
37
46
|
int b;
|
|
@@ -53,6 +62,60 @@ typedef struct BFG {
|
|
|
53
62
|
double g;
|
|
54
63
|
} inner;
|
|
55
64
|
} BFG;
|
|
65
|
+
#pragma pack(push, 1)
|
|
66
|
+
typedef struct PackedBFG {
|
|
67
|
+
int8_t a;
|
|
68
|
+
int64_t b;
|
|
69
|
+
signed char c;
|
|
70
|
+
const char *d;
|
|
71
|
+
short e;
|
|
72
|
+
struct {
|
|
73
|
+
float f;
|
|
74
|
+
double g;
|
|
75
|
+
} inner;
|
|
76
|
+
} PackedBFG;
|
|
77
|
+
#pragma pack(pop)
|
|
78
|
+
|
|
79
|
+
EXPORT void FillPack1(int a, Pack1 *p)
|
|
80
|
+
{
|
|
81
|
+
p->a = a;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
EXPORT Pack1 RetPack1(int a)
|
|
85
|
+
{
|
|
86
|
+
Pack1 p;
|
|
87
|
+
|
|
88
|
+
p.a = a;
|
|
89
|
+
|
|
90
|
+
return p;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
EXPORT void FASTCALL AddPack1(int a, Pack1 *p)
|
|
94
|
+
{
|
|
95
|
+
p->a += a;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
EXPORT void FillPack2(int a, int b, Pack2 *p)
|
|
99
|
+
{
|
|
100
|
+
p->a = a;
|
|
101
|
+
p->b = b;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
EXPORT Pack2 RetPack2(int a, int b)
|
|
105
|
+
{
|
|
106
|
+
Pack2 p;
|
|
107
|
+
|
|
108
|
+
p.a = a;
|
|
109
|
+
p.b = b;
|
|
110
|
+
|
|
111
|
+
return p;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
EXPORT void FASTCALL AddPack2(int a, int b, Pack2 *p)
|
|
115
|
+
{
|
|
116
|
+
p->a += a;
|
|
117
|
+
p->b += b;
|
|
118
|
+
}
|
|
56
119
|
|
|
57
120
|
EXPORT void FillPack3(int a, int b, int c, Pack3 *p)
|
|
58
121
|
{
|
|
@@ -64,7 +127,7 @@ EXPORT void FillPack3(int a, int b, int c, Pack3 *p)
|
|
|
64
127
|
EXPORT Pack3 RetPack3(int a, int b, int c)
|
|
65
128
|
{
|
|
66
129
|
Pack3 p;
|
|
67
|
-
|
|
130
|
+
|
|
68
131
|
p.a = a;
|
|
69
132
|
p.b = b;
|
|
70
133
|
p.c = c;
|
|
@@ -135,14 +198,17 @@ EXPORT const char *ConcatenateToStr8(int64_t a, int64_t b, int64_t c, int64_t d,
|
|
|
135
198
|
return buf;
|
|
136
199
|
}
|
|
137
200
|
|
|
138
|
-
EXPORT BFG STDCALL MakeBFG(int x, double y,
|
|
201
|
+
EXPORT BFG STDCALL MakeBFG(BFG *p, int x, double y, const char *str)
|
|
139
202
|
{
|
|
140
203
|
BFG bfg;
|
|
141
204
|
|
|
205
|
+
char buf[64];
|
|
206
|
+
snprintf(buf, sizeof(buf), "X/%s/X", str);
|
|
207
|
+
|
|
142
208
|
bfg.a = x;
|
|
143
209
|
bfg.b = x * 2;
|
|
144
210
|
bfg.c = x - 27;
|
|
145
|
-
bfg.d =
|
|
211
|
+
bfg.d = buf;
|
|
146
212
|
bfg.e = x * 27;
|
|
147
213
|
bfg.inner.f = (float)y * x;
|
|
148
214
|
bfg.inner.g = (double)y - x;
|
|
@@ -150,3 +216,32 @@ EXPORT BFG STDCALL MakeBFG(int x, double y, BFG *p)
|
|
|
150
216
|
|
|
151
217
|
return bfg;
|
|
152
218
|
}
|
|
219
|
+
|
|
220
|
+
EXPORT PackedBFG FASTCALL MakePackedBFG(int x, double y, PackedBFG *p, const char *str)
|
|
221
|
+
{
|
|
222
|
+
PackedBFG bfg;
|
|
223
|
+
|
|
224
|
+
char buf[64];
|
|
225
|
+
snprintf(buf, sizeof(buf), "X/%s/X", str);
|
|
226
|
+
|
|
227
|
+
bfg.a = x;
|
|
228
|
+
bfg.b = x * 2;
|
|
229
|
+
bfg.c = x - 27;
|
|
230
|
+
bfg.d = buf;
|
|
231
|
+
bfg.e = x * 27;
|
|
232
|
+
bfg.inner.f = (float)y * x;
|
|
233
|
+
bfg.inner.g = (double)y - x;
|
|
234
|
+
*p = bfg;
|
|
235
|
+
|
|
236
|
+
return bfg;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
EXPORT const char *ReturnBigString(const char *str)
|
|
240
|
+
{
|
|
241
|
+
static char buf[16 * 1024 * 1024];
|
|
242
|
+
|
|
243
|
+
size_t len = strlen(str);
|
|
244
|
+
memcpy(buf, str, len + 1);
|
|
245
|
+
|
|
246
|
+
return buf;
|
|
247
|
+
}
|
package/test/tests/misc.js
CHANGED
|
@@ -17,6 +17,13 @@ const koffi = require('../build/koffi.node');
|
|
|
17
17
|
const assert = require('assert');
|
|
18
18
|
const path = require('path');
|
|
19
19
|
|
|
20
|
+
const Pack1 = koffi.struct('Pack1', {
|
|
21
|
+
a: 'int'
|
|
22
|
+
});
|
|
23
|
+
const Pack2 = koffi.struct('Pack2', {
|
|
24
|
+
a: 'int',
|
|
25
|
+
b: 'int'
|
|
26
|
+
});
|
|
20
27
|
const Pack3 = koffi.struct('Pack3', {
|
|
21
28
|
a: 'int',
|
|
22
29
|
b: 'int',
|
|
@@ -29,7 +36,18 @@ const BFG = koffi.struct('BFG', {
|
|
|
29
36
|
c: 'char',
|
|
30
37
|
d: 'string',
|
|
31
38
|
e: 'short',
|
|
32
|
-
inner: koffi.struct(
|
|
39
|
+
inner: koffi.struct({
|
|
40
|
+
f: 'float',
|
|
41
|
+
g: 'double'
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
const PackedBFG = koffi.pack('PackedBFG', {
|
|
45
|
+
a: 'int8_t',
|
|
46
|
+
b: 'int64_t',
|
|
47
|
+
c: 'char',
|
|
48
|
+
d: 'string',
|
|
49
|
+
e: 'short',
|
|
50
|
+
inner: koffi.pack({
|
|
33
51
|
f: 'float',
|
|
34
52
|
g: 'double'
|
|
35
53
|
})
|
|
@@ -51,6 +69,12 @@ async function test() {
|
|
|
51
69
|
let lib_filename = path.dirname(__filename) + '/../build/misc' + koffi.extension;
|
|
52
70
|
let lib = koffi.load(lib_filename);
|
|
53
71
|
|
|
72
|
+
const FillPack1 = lib.cdecl('FillPack1', 'void', ['int', koffi.out(koffi.pointer(Pack1))]);
|
|
73
|
+
const RetPack1 = lib.cdecl('RetPack1', Pack1, ['int']);
|
|
74
|
+
const AddPack1 = lib.fastcall('AddPack1', 'void', ['int', koffi.inout(koffi.pointer(Pack1))]);
|
|
75
|
+
const FillPack2 = lib.cdecl('FillPack2', 'void', ['int', 'int', koffi.out(koffi.pointer(Pack2))]);
|
|
76
|
+
const RetPack2 = lib.cdecl('RetPack2', Pack2, ['int', 'int']);
|
|
77
|
+
const AddPack2 = lib.fastcall('AddPack2', 'void', ['int', 'int', koffi.inout(koffi.pointer(Pack2))]);
|
|
54
78
|
const FillPack3 = lib.cdecl('FillPack3', 'void', ['int', 'int', 'int', koffi.out(koffi.pointer(Pack3))]);
|
|
55
79
|
const RetPack3 = lib.cdecl('RetPack3', Pack3, ['int', 'int', 'int']);
|
|
56
80
|
const AddPack3 = lib.fastcall('AddPack3', 'void', ['int', 'int', 'int', koffi.inout(koffi.pointer(Pack3))]);
|
|
@@ -60,28 +84,81 @@ async function test() {
|
|
|
60
84
|
const ConcatenateToStr1 = lib.cdecl('ConcatenateToStr1', 'string', [...Array(8).fill('int8_t'), koffi.struct('IJK1', {i: 'int8_t', j: 'int8_t', k: 'int8_t'}), 'int8_t']);
|
|
61
85
|
const ConcatenateToStr4 = lib.cdecl('ConcatenateToStr4', 'string', [...Array(8).fill('int32_t'), koffi.pointer(koffi.struct('IJK4', {i: 'int32_t', j: 'int32_t', k: 'int32_t'})), 'int32_t']);
|
|
62
86
|
const ConcatenateToStr8 = lib.cdecl('ConcatenateToStr8', 'string', [...Array(8).fill('int64_t'), koffi.struct('IJK8', {i: 'int64_t', j: 'int64_t', k: 'int64_t'}), 'int64_t']);
|
|
63
|
-
const MakeBFG = lib.stdcall('MakeBFG', BFG, [
|
|
87
|
+
const MakeBFG = lib.stdcall('MakeBFG', BFG, [koffi.out(koffi.pointer(BFG)), 'int', 'double', 'string']);
|
|
88
|
+
const MakePackedBFG = lib.fastcall('MakePackedBFG', PackedBFG, ['int', 'double', koffi.out(koffi.pointer(PackedBFG)), 'string']);
|
|
89
|
+
const ReturnBigString = lib.stdcall('ReturnBigString', 'string', ['string']);
|
|
64
90
|
|
|
65
|
-
|
|
91
|
+
// Simple tests with Pack1
|
|
92
|
+
{
|
|
93
|
+
let p = {};
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
FillPack1(777, p);
|
|
96
|
+
assert.deepEqual(p, { a: 777 });
|
|
97
|
+
|
|
98
|
+
let q = RetPack1(6);
|
|
99
|
+
assert.deepEqual(q, { a: 6 });
|
|
100
|
+
|
|
101
|
+
AddPack1(6, p);
|
|
102
|
+
assert.deepEqual(p, { a: 783 });
|
|
103
|
+
}
|
|
69
104
|
|
|
70
|
-
|
|
71
|
-
|
|
105
|
+
// Simple tests with Pack2
|
|
106
|
+
{
|
|
107
|
+
let p = {};
|
|
72
108
|
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
FillPack2(123, 456, p);
|
|
110
|
+
assert.deepEqual(p, { a: 123, b: 456 });
|
|
75
111
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assert.equal(ConcatenateToInt8(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
79
|
-
assert.equal(ConcatenateToStr1(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
80
|
-
assert.equal(ConcatenateToStr4(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
81
|
-
assert.equal(ConcatenateToStr8(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
112
|
+
let q = RetPack2(6, 9);
|
|
113
|
+
assert.deepEqual(q, { a: 6, b: 9 });
|
|
82
114
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
AddPack2(6, 9, p);
|
|
116
|
+
assert.deepEqual(p, { a: 129, b: 465 });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Simple tests with Pack3
|
|
120
|
+
{
|
|
121
|
+
let p = {};
|
|
122
|
+
|
|
123
|
+
FillPack3(1, 2, 3, p);
|
|
124
|
+
assert.deepEqual(p, { a: 1, b: 2, c: 3 });
|
|
125
|
+
|
|
126
|
+
let q = RetPack3(6, 9, -12);
|
|
127
|
+
assert.deepEqual(q, { a: 6, b: 9, c: -12 });
|
|
128
|
+
|
|
129
|
+
AddPack3(6, 9, -12, p);
|
|
130
|
+
assert.deepEqual(p, { a: 7, b: 11, c: -9 });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Many parameters
|
|
134
|
+
{
|
|
135
|
+
assert.equal(ConcatenateToInt1(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
136
|
+
assert.equal(ConcatenateToInt4(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
137
|
+
assert.equal(ConcatenateToInt8(5, 6, 1, 2, 3, 9, 4, 4, 0, 6, 8, 7), 561239440687n);
|
|
138
|
+
assert.equal(ConcatenateToStr1(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
139
|
+
assert.equal(ConcatenateToStr4(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
140
|
+
assert.equal(ConcatenateToStr8(5, 6, 1, 2, 3, 9, 4, 4, {i: 0, j: 6, k: 8}, 7), '561239440687');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Big struct
|
|
144
|
+
{
|
|
145
|
+
let out = {};
|
|
146
|
+
let bfg = MakeBFG(out, 2, 7, '__Hello123456789++++foobarFOOBAR!__');
|
|
147
|
+
assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
|
|
148
|
+
assert.deepEqual(out, bfg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Packed struct
|
|
152
|
+
{
|
|
153
|
+
let out = {};
|
|
154
|
+
let bfg = MakePackedBFG(2, 7, out, '__Hello123456789++++foobarFOOBAR!__');
|
|
155
|
+
assert.deepEqual(bfg, { a: 2, b: 4, c: -25, d: 'X/__Hello123456789++++foobarFOOBAR!__/X', e: 54, inner: { f: 14, g: 5 } });
|
|
156
|
+
assert.deepEqual(out, bfg);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Big string
|
|
160
|
+
{
|
|
161
|
+
let str = 'fooBAR!'.repeat(1024 * 1024);
|
|
162
|
+
assert.equal(ReturnBigString(str), str);
|
|
163
|
+
}
|
|
87
164
|
}
|