koffi 1.0.3 → 1.0.4
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/README.md +22 -21
- package/build/qemu/1.0.4/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.4/koffi_win32_x64.tar.gz +0 -0
- package/package.json +1 -1
- package/src/abi_arm32.cc +119 -211
- package/src/abi_arm64.cc +92 -113
- package/src/abi_x64_sysv.cc +106 -131
- package/src/abi_x64_win.cc +78 -94
- package/src/abi_x86.cc +80 -95
- package/src/call.cc +144 -19
- package/src/call.hh +39 -3
- package/src/ffi.cc +11 -2
- package/src/ffi.hh +6 -12
- package/src/util.cc +0 -127
- package/src/util.hh +0 -16
- package/test/misc.c +66 -0
- package/test/misc.js +47 -0
- package/vendor/libcc/libcc.hh +1 -1
- package/build/qemu/1.0.3/koffi_darwin_x64.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_freebsd_arm64.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_freebsd_ia32.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_freebsd_x64.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_linux_arm.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_linux_arm64.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_linux_ia32.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_linux_x64.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_win32_ia32.tar.gz +0 -0
- package/build/qemu/1.0.3/koffi_win32_x64.tar.gz +0 -0
package/src/abi_x86.cc
CHANGED
|
@@ -82,33 +82,24 @@ bool AnalyseFunction(InstanceData *instance, FunctionInfo *func)
|
|
|
82
82
|
return true;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
bool CallData::Prepare(const Napi::CallbackInfo &info)
|
|
86
86
|
{
|
|
87
|
-
// Sanity checks
|
|
88
|
-
if (info.Length() < (uint32_t)func->parameters.len) {
|
|
89
|
-
ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
|
|
90
|
-
return env.Null();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
uint8_t *return_ptr = nullptr;
|
|
94
87
|
uint32_t *args_ptr = nullptr;
|
|
95
88
|
uint32_t *fast_ptr = nullptr;
|
|
96
89
|
|
|
97
90
|
// Pass return value in register or through memory
|
|
98
91
|
if (RG_UNLIKELY(!AllocStack(func->args_size, 16, &args_ptr)))
|
|
99
|
-
return
|
|
92
|
+
return false;
|
|
100
93
|
if (func->convention == CallConvention::Fastcall) {
|
|
101
94
|
fast_ptr = args_ptr;
|
|
102
95
|
args_ptr += 4;
|
|
103
96
|
}
|
|
104
97
|
if (!func->ret.trivial) {
|
|
105
98
|
if (RG_UNLIKELY(!AllocHeap(func->ret.type->size, 16, &return_ptr)))
|
|
106
|
-
return
|
|
99
|
+
return false;
|
|
107
100
|
*((func->ret.fast ? fast_ptr : args_ptr)++) = (uint32_t)return_ptr;
|
|
108
101
|
}
|
|
109
102
|
|
|
110
|
-
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
111
|
-
|
|
112
103
|
// Push arguments
|
|
113
104
|
for (Size i = 0; i < func->parameters.len; i++) {
|
|
114
105
|
const ParameterInfo ¶m = func->parameters[i];
|
|
@@ -122,7 +113,7 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
122
113
|
case PrimitiveKind::Bool: {
|
|
123
114
|
if (RG_UNLIKELY(!value.IsBoolean())) {
|
|
124
115
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected boolean", GetValueType(instance, value), i + 1);
|
|
125
|
-
return
|
|
116
|
+
return false;
|
|
126
117
|
}
|
|
127
118
|
|
|
128
119
|
bool b = value.As<Napi::Boolean>();
|
|
@@ -136,7 +127,7 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
136
127
|
case PrimitiveKind::UInt32: {
|
|
137
128
|
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
138
129
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
139
|
-
return
|
|
130
|
+
return false;
|
|
140
131
|
}
|
|
141
132
|
|
|
142
133
|
int32_t v = CopyNumber<int32_t>(value);
|
|
@@ -146,43 +137,24 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
146
137
|
case PrimitiveKind::UInt64: {
|
|
147
138
|
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
148
139
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
149
|
-
return
|
|
140
|
+
return false;
|
|
150
141
|
}
|
|
151
142
|
|
|
152
143
|
int64_t v = CopyNumber<int64_t>(value);
|
|
153
144
|
*(uint64_t *)args_ptr = (uint64_t)v;
|
|
154
145
|
args_ptr += 2;
|
|
155
146
|
} break;
|
|
156
|
-
case PrimitiveKind::Float32: {
|
|
157
|
-
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
158
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
159
|
-
return env.Null();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
float f = CopyNumber<float>(value);
|
|
163
|
-
*(float *)((param.fast ? fast_ptr : args_ptr)++) = f;
|
|
164
|
-
} break;
|
|
165
|
-
case PrimitiveKind::Float64: {
|
|
166
|
-
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
167
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
168
|
-
return env.Null();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
double d = CopyNumber<double>(value);
|
|
172
|
-
*(double *)args_ptr = d;
|
|
173
|
-
args_ptr += 2;
|
|
174
|
-
} break;
|
|
175
147
|
case PrimitiveKind::String: {
|
|
176
148
|
const char *str;
|
|
177
149
|
if (RG_LIKELY(value.IsString())) {
|
|
178
150
|
str = PushString(value);
|
|
179
151
|
if (RG_UNLIKELY(!str))
|
|
180
|
-
return
|
|
152
|
+
return false;
|
|
181
153
|
} else if (IsNullOrUndefined(value)) {
|
|
182
154
|
str = nullptr;
|
|
183
155
|
} else {
|
|
184
156
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
|
|
185
|
-
return
|
|
157
|
+
return false;
|
|
186
158
|
}
|
|
187
159
|
|
|
188
160
|
*(const char **)((param.fast ? fast_ptr : args_ptr)++) = str;
|
|
@@ -192,12 +164,12 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
192
164
|
if (RG_LIKELY(value.IsString())) {
|
|
193
165
|
str16 = PushString16(value);
|
|
194
166
|
if (RG_UNLIKELY(!str16))
|
|
195
|
-
return
|
|
167
|
+
return false;
|
|
196
168
|
} else if (IsNullOrUndefined(value)) {
|
|
197
169
|
str16 = nullptr;
|
|
198
170
|
} else {
|
|
199
171
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
|
|
200
|
-
return
|
|
172
|
+
return false;
|
|
201
173
|
}
|
|
202
174
|
|
|
203
175
|
*(const char16_t **)((param.fast ? fast_ptr : args_ptr)++) = str16;
|
|
@@ -211,11 +183,11 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
211
183
|
Napi::Object obj = value.As<Napi::Object>();
|
|
212
184
|
|
|
213
185
|
if (RG_UNLIKELY(!AllocHeap(param.type->ref->size, 16, &ptr)))
|
|
214
|
-
return
|
|
186
|
+
return false;
|
|
215
187
|
|
|
216
188
|
if (param.directions & 1) {
|
|
217
189
|
if (!PushObject(obj, param.type->ref, ptr))
|
|
218
|
-
return
|
|
190
|
+
return false;
|
|
219
191
|
} else {
|
|
220
192
|
memset(ptr, 0, param.type->size);
|
|
221
193
|
}
|
|
@@ -227,16 +199,15 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
227
199
|
ptr = nullptr;
|
|
228
200
|
} else {
|
|
229
201
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
|
|
230
|
-
return
|
|
202
|
+
return false;
|
|
231
203
|
}
|
|
232
204
|
|
|
233
205
|
*(uint8_t **)((param.fast ? fast_ptr : args_ptr)++) = ptr;
|
|
234
206
|
} break;
|
|
235
|
-
|
|
236
207
|
case PrimitiveKind::Record: {
|
|
237
208
|
if (RG_UNLIKELY(!IsObject(value))) {
|
|
238
209
|
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected object", GetValueType(instance, value), i + 1);
|
|
239
|
-
return
|
|
210
|
+
return false;
|
|
240
211
|
}
|
|
241
212
|
|
|
242
213
|
Napi::Object obj = value.As<Napi::Object>();
|
|
@@ -244,93 +215,107 @@ Napi::Value CallData::Execute(const Napi::CallbackInfo &info)
|
|
|
244
215
|
if (param.fast) {
|
|
245
216
|
uint8_t *ptr = (uint8_t *)(fast_ptr++);
|
|
246
217
|
if (!PushObject(obj, param.type, ptr))
|
|
247
|
-
return
|
|
218
|
+
return false;
|
|
248
219
|
} else {
|
|
249
220
|
uint8_t *ptr = (uint8_t *)AlignUp(args_ptr, param.type->align);
|
|
250
221
|
if (!PushObject(obj, param.type, ptr))
|
|
251
|
-
return
|
|
222
|
+
return false;
|
|
252
223
|
args_ptr = (uint32_t *)AlignUp(ptr + param.type->size, 4);
|
|
253
224
|
}
|
|
254
225
|
} break;
|
|
226
|
+
case PrimitiveKind::Float32: {
|
|
227
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
228
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
float f = CopyNumber<float>(value);
|
|
233
|
+
*(float *)((param.fast ? fast_ptr : args_ptr)++) = f;
|
|
234
|
+
} break;
|
|
235
|
+
case PrimitiveKind::Float64: {
|
|
236
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
237
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
double d = CopyNumber<double>(value);
|
|
242
|
+
*(double *)args_ptr = d;
|
|
243
|
+
args_ptr += 2;
|
|
244
|
+
} break;
|
|
255
245
|
}
|
|
256
246
|
}
|
|
257
247
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
261
250
|
|
|
251
|
+
void CallData::Execute()
|
|
252
|
+
{
|
|
262
253
|
#define PERFORM_CALL(Suffix) \
|
|
263
254
|
([&]() { \
|
|
264
255
|
auto ret = (func->convention == CallConvention::Fastcall ? ForwardCallR ## Suffix(func->func, GetSP()) \
|
|
265
256
|
: ForwardCall ## Suffix(func->func, GetSP())); \
|
|
266
|
-
PopOutArguments(out_objects); \
|
|
267
257
|
return ret; \
|
|
268
258
|
})()
|
|
269
259
|
|
|
270
260
|
// Execute and convert return value
|
|
271
261
|
switch (func->ret.type->primitive) {
|
|
272
|
-
case PrimitiveKind::Void:
|
|
273
|
-
|
|
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;
|
|
262
|
+
case PrimitiveKind::Void:
|
|
263
|
+
case PrimitiveKind::Bool:
|
|
280
264
|
case PrimitiveKind::Int8:
|
|
281
265
|
case PrimitiveKind::UInt8:
|
|
282
266
|
case PrimitiveKind::Int16:
|
|
283
267
|
case PrimitiveKind::UInt16:
|
|
284
268
|
case PrimitiveKind::Int32:
|
|
285
|
-
case PrimitiveKind::UInt32:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
case PrimitiveKind::
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
} break;
|
|
293
|
-
case PrimitiveKind::
|
|
294
|
-
|
|
295
|
-
return Napi::BigInt::New(env, ret);
|
|
296
|
-
} break;
|
|
297
|
-
case PrimitiveKind::Float32: {
|
|
298
|
-
float f = PERFORM_CALL(F);
|
|
299
|
-
return Napi::Number::New(env, (double)f);
|
|
300
|
-
} break;
|
|
301
|
-
case PrimitiveKind::Float64: {
|
|
302
|
-
double d = PERFORM_CALL(D);
|
|
303
|
-
return Napi::Number::New(env, d);
|
|
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;
|
|
269
|
+
case PrimitiveKind::UInt32:
|
|
270
|
+
case PrimitiveKind::Int64:
|
|
271
|
+
case PrimitiveKind::UInt64:
|
|
272
|
+
case PrimitiveKind::String:
|
|
273
|
+
case PrimitiveKind::String16:
|
|
274
|
+
case PrimitiveKind::Pointer:
|
|
275
|
+
case PrimitiveKind::Record: { result.u64 = PERFORM_CALL(G); } break;
|
|
276
|
+
case PrimitiveKind::Float32: { result.f = PERFORM_CALL(F); } break;
|
|
277
|
+
case PrimitiveKind::Float64: { result.d = PERFORM_CALL(D); } break;
|
|
278
|
+
}
|
|
316
279
|
|
|
317
|
-
|
|
280
|
+
#undef PERFORM_CALL
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
Napi::Value CallData::Complete()
|
|
284
|
+
{
|
|
285
|
+
for (const OutObject &obj: out_objects) {
|
|
286
|
+
PopObject(obj.obj, obj.ptr, obj.type);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
switch (func->ret.type->primitive) {
|
|
290
|
+
case PrimitiveKind::Void: return env.Null();
|
|
291
|
+
case PrimitiveKind::Bool: return Napi::Boolean::New(env, result.u32);
|
|
292
|
+
case PrimitiveKind::Int8:
|
|
293
|
+
case PrimitiveKind::UInt8:
|
|
294
|
+
case PrimitiveKind::Int16:
|
|
295
|
+
case PrimitiveKind::UInt16:
|
|
296
|
+
case PrimitiveKind::Int32:
|
|
297
|
+
case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)result.u32);
|
|
298
|
+
case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)result.u64);
|
|
299
|
+
case PrimitiveKind::UInt64: return Napi::BigInt::New(env, result.u64);
|
|
300
|
+
case PrimitiveKind::String: return Napi::String::New(env, (const char *)result.ptr);
|
|
301
|
+
case PrimitiveKind::String16: return Napi::String::New(env, (const char16_t *)result.ptr);
|
|
302
|
+
case PrimitiveKind::Pointer: {
|
|
303
|
+
Napi::External<void> external = Napi::External<void>::New(env, result.ptr);
|
|
318
304
|
SetValueTag(instance, external, func->ret.type);
|
|
319
305
|
|
|
320
306
|
return external;
|
|
321
307
|
} break;
|
|
322
|
-
|
|
323
308
|
case PrimitiveKind::Record: {
|
|
324
|
-
|
|
325
|
-
|
|
309
|
+
const uint8_t *ptr = return_ptr ? (const uint8_t *)return_ptr
|
|
310
|
+
: (const uint8_t *)&result.buf;
|
|
326
311
|
|
|
327
|
-
Napi::Object obj = PopObject(
|
|
312
|
+
Napi::Object obj = PopObject(ptr, func->ret.type);
|
|
328
313
|
return obj;
|
|
329
314
|
} break;
|
|
315
|
+
case PrimitiveKind::Float32: return Napi::Number::New(env, (double)result.f);
|
|
316
|
+
case PrimitiveKind::Float64: return Napi::Number::New(env, result.d);
|
|
330
317
|
}
|
|
331
318
|
|
|
332
|
-
#undef PERFORM_CALL
|
|
333
|
-
|
|
334
319
|
RG_UNREACHABLE();
|
|
335
320
|
}
|
|
336
321
|
|
package/src/call.cc
CHANGED
|
@@ -156,24 +156,6 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
156
156
|
int64_t v = CopyNumber<int64_t>(value);
|
|
157
157
|
memcpy(dest, &v, member.type->size); // Little Endian
|
|
158
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
159
|
case PrimitiveKind::String: {
|
|
178
160
|
if (RG_UNLIKELY(!value.IsString())) {
|
|
179
161
|
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected string", GetValueType(instance, value), member.name);
|
|
@@ -206,7 +188,6 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
206
188
|
void *ptr = external.Data();
|
|
207
189
|
*(void **)dest = ptr;
|
|
208
190
|
} break;
|
|
209
|
-
|
|
210
191
|
case PrimitiveKind::Record: {
|
|
211
192
|
if (RG_UNLIKELY(!IsObject(value))) {
|
|
212
193
|
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected object", GetValueType(instance, value), member.name);
|
|
@@ -217,6 +198,24 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
217
198
|
if (!PushObject(obj, member.type, dest))
|
|
218
199
|
return false;
|
|
219
200
|
} break;
|
|
201
|
+
case PrimitiveKind::Float32: {
|
|
202
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
203
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
float f = CopyNumber<float>(value);
|
|
208
|
+
memcpy(dest, &f, 4);
|
|
209
|
+
} break;
|
|
210
|
+
case PrimitiveKind::Float64: {
|
|
211
|
+
if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
|
|
212
|
+
ThrowError<Napi::TypeError>(env, "Unexpected value %1 for member '%2', expected number", GetValueType(instance, value), member.name);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
double d = CopyNumber<double>(value);
|
|
217
|
+
memcpy(dest, &d, 8);
|
|
218
|
+
} break;
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
dest += member.type->size;
|
|
@@ -225,4 +224,130 @@ bool CallData::PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t
|
|
|
225
224
|
return true;
|
|
226
225
|
}
|
|
227
226
|
|
|
227
|
+
void CallData::PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type)
|
|
228
|
+
{
|
|
229
|
+
Napi::Env env = obj.Env();
|
|
230
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
231
|
+
|
|
232
|
+
RG_ASSERT(type->primitive == PrimitiveKind::Record);
|
|
233
|
+
|
|
234
|
+
for (const RecordMember &member: type->members) {
|
|
235
|
+
ptr = AlignUp(ptr, member.align);
|
|
236
|
+
|
|
237
|
+
switch (member.type->primitive) {
|
|
238
|
+
case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
|
|
239
|
+
|
|
240
|
+
case PrimitiveKind::Bool: {
|
|
241
|
+
bool b = *(bool *)ptr;
|
|
242
|
+
obj.Set(member.name, Napi::Boolean::New(env, b));
|
|
243
|
+
} break;
|
|
244
|
+
case PrimitiveKind::Int8: {
|
|
245
|
+
double d = (double)*(int8_t *)ptr;
|
|
246
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
247
|
+
} break;
|
|
248
|
+
case PrimitiveKind::UInt8: {
|
|
249
|
+
double d = (double)*(uint8_t *)ptr;
|
|
250
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
251
|
+
} break;
|
|
252
|
+
case PrimitiveKind::Int16: {
|
|
253
|
+
double d = (double)*(int16_t *)ptr;
|
|
254
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
255
|
+
} break;
|
|
256
|
+
case PrimitiveKind::UInt16: {
|
|
257
|
+
double d = (double)*(uint16_t *)ptr;
|
|
258
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
259
|
+
} break;
|
|
260
|
+
case PrimitiveKind::Int32: {
|
|
261
|
+
double d = (double)*(int32_t *)ptr;
|
|
262
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
263
|
+
} break;
|
|
264
|
+
case PrimitiveKind::UInt32: {
|
|
265
|
+
double d = (double)*(uint32_t *)ptr;
|
|
266
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
267
|
+
} break;
|
|
268
|
+
case PrimitiveKind::Int64: {
|
|
269
|
+
int64_t v = *(int64_t *)ptr;
|
|
270
|
+
obj.Set(member.name, Napi::BigInt::New(env, v));
|
|
271
|
+
} break;
|
|
272
|
+
case PrimitiveKind::UInt64: {
|
|
273
|
+
uint64_t v = *(uint64_t *)ptr;
|
|
274
|
+
obj.Set(member.name, Napi::BigInt::New(env, v));
|
|
275
|
+
} break;
|
|
276
|
+
case PrimitiveKind::String: {
|
|
277
|
+
const char *str = *(const char **)ptr;
|
|
278
|
+
obj.Set(member.name, Napi::String::New(env, str));
|
|
279
|
+
} break;
|
|
280
|
+
case PrimitiveKind::String16: {
|
|
281
|
+
const char16_t *str16 = *(const char16_t **)ptr;
|
|
282
|
+
obj.Set(member.name, Napi::String::New(env, str16));
|
|
283
|
+
} break;
|
|
284
|
+
case PrimitiveKind::Pointer: {
|
|
285
|
+
void *ptr2 = *(void **)ptr;
|
|
286
|
+
|
|
287
|
+
Napi::External<void> external = Napi::External<void>::New(env, ptr2);
|
|
288
|
+
SetValueTag(instance, external, member.type);
|
|
289
|
+
|
|
290
|
+
obj.Set(member.name, external);
|
|
291
|
+
} break;
|
|
292
|
+
case PrimitiveKind::Record: {
|
|
293
|
+
Napi::Object obj2 = PopObject(ptr, member.type);
|
|
294
|
+
obj.Set(member.name, obj2);
|
|
295
|
+
} break;
|
|
296
|
+
case PrimitiveKind::Float32: {
|
|
297
|
+
float f;
|
|
298
|
+
memcpy(&f, ptr, 4);
|
|
299
|
+
obj.Set(member.name, Napi::Number::New(env, (double)f));
|
|
300
|
+
} break;
|
|
301
|
+
case PrimitiveKind::Float64: {
|
|
302
|
+
double d;
|
|
303
|
+
memcpy(&d, ptr, 8);
|
|
304
|
+
obj.Set(member.name, Napi::Number::New(env, d));
|
|
305
|
+
} break;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
ptr += member.type->size;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Napi::Object CallData::PopObject(const uint8_t *ptr, const TypeInfo *type)
|
|
313
|
+
{
|
|
314
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
315
|
+
PopObject(obj, ptr, type);
|
|
316
|
+
return obj;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
static void DumpMemory(const char *type, Span<const uint8_t> bytes)
|
|
320
|
+
{
|
|
321
|
+
if (bytes.len) {
|
|
322
|
+
PrintLn(stderr, "%1 at 0x%2 (%3):", type, bytes.ptr, FmtMemSize(bytes.len));
|
|
323
|
+
|
|
324
|
+
for (const uint8_t *ptr = bytes.begin(); ptr < bytes.end();) {
|
|
325
|
+
Print(stderr, " [0x%1 %2 %3] ", FmtArg(ptr).Pad0(-16),
|
|
326
|
+
FmtArg((ptr - bytes.begin()) / sizeof(void *)).Pad(-4),
|
|
327
|
+
FmtArg(ptr - bytes.begin()).Pad(-4));
|
|
328
|
+
for (int i = 0; ptr < bytes.end() && i < (int)sizeof(void *); i++, ptr++) {
|
|
329
|
+
Print(stderr, " %1", FmtHex(*ptr).Pad0(-2));
|
|
330
|
+
}
|
|
331
|
+
PrintLn(stderr);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
void CallData::DumpDebug() const
|
|
337
|
+
{
|
|
338
|
+
PrintLn(stderr, "%!..+---- %1 (%2) ----%!0", func->name, CallConventionNames[(int)func->convention]);
|
|
339
|
+
|
|
340
|
+
if (func->parameters.len) {
|
|
341
|
+
PrintLn(stderr, "Parameters:");
|
|
342
|
+
for (Size i = 0; i < func->parameters.len; i++) {
|
|
343
|
+
const ParameterInfo ¶m = func->parameters[i];
|
|
344
|
+
PrintLn(stderr, " %1 = %2 (%3)", i, param.type->name, FmtMemSize(param.type->size));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
PrintLn(stderr, "Return: %1 (%2)", func->ret.type->name, FmtMemSize(func->ret.type->size));
|
|
348
|
+
|
|
349
|
+
DumpMemory("Stack", GetStack());
|
|
350
|
+
DumpMemory("Heap", GetHeap());
|
|
351
|
+
}
|
|
352
|
+
|
|
228
353
|
}
|
package/src/call.hh
CHANGED
|
@@ -28,13 +28,30 @@ class CallData {
|
|
|
28
28
|
InstanceData *instance;
|
|
29
29
|
const FunctionInfo *func;
|
|
30
30
|
|
|
31
|
+
struct OutObject {
|
|
32
|
+
Napi::Object obj;
|
|
33
|
+
const uint8_t *ptr;
|
|
34
|
+
const TypeInfo *type;
|
|
35
|
+
};
|
|
36
|
+
|
|
31
37
|
Span<uint8_t> *stack_mem;
|
|
32
38
|
Span<uint8_t> *heap_mem;
|
|
39
|
+
LocalArray<OutObject, MaxOutParameters> out_objects;
|
|
33
40
|
BlockAllocator big_alloc;
|
|
34
41
|
|
|
35
42
|
Span<uint8_t> old_stack_mem;
|
|
36
43
|
Span<uint8_t> old_heap_mem;
|
|
37
44
|
|
|
45
|
+
union {
|
|
46
|
+
uint32_t u32;
|
|
47
|
+
uint64_t u64;
|
|
48
|
+
float f;
|
|
49
|
+
double d;
|
|
50
|
+
void *ptr;
|
|
51
|
+
uint8_t buf[32];
|
|
52
|
+
} result;
|
|
53
|
+
uint8_t *return_ptr = nullptr;
|
|
54
|
+
|
|
38
55
|
public:
|
|
39
56
|
CallData(Napi::Env env, InstanceData *instance, const FunctionInfo *func);
|
|
40
57
|
~CallData();
|
|
@@ -56,6 +73,26 @@ public:
|
|
|
56
73
|
return MakeSpan(ptr, len);
|
|
57
74
|
}
|
|
58
75
|
|
|
76
|
+
bool Prepare(const Napi::CallbackInfo &info);
|
|
77
|
+
void Execute();
|
|
78
|
+
Napi::Value Complete();
|
|
79
|
+
|
|
80
|
+
Napi::Value Run(const Napi::CallbackInfo &info)
|
|
81
|
+
{
|
|
82
|
+
if (!RG_UNLIKELY(Prepare(info)))
|
|
83
|
+
return env.Null();
|
|
84
|
+
|
|
85
|
+
if (instance->debug) {
|
|
86
|
+
DumpDebug();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Execute();
|
|
90
|
+
return Complete();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void DumpDebug() const;
|
|
94
|
+
|
|
95
|
+
private:
|
|
59
96
|
template <typename T = void>
|
|
60
97
|
bool AllocStack(Size size, Size align, T **out_ptr = nullptr);
|
|
61
98
|
template <typename T = void>
|
|
@@ -65,9 +102,8 @@ public:
|
|
|
65
102
|
const char16_t *PushString16(const Napi::Value &value);
|
|
66
103
|
bool PushObject(const Napi::Object &obj, const TypeInfo *type, uint8_t *dest);
|
|
67
104
|
|
|
68
|
-
void
|
|
69
|
-
|
|
70
|
-
Napi::Value Execute(const Napi::CallbackInfo &info);
|
|
105
|
+
void PopObject(Napi::Object obj, const uint8_t *ptr, const TypeInfo *type);
|
|
106
|
+
Napi::Object PopObject(const uint8_t *ptr, const TypeInfo *type);
|
|
71
107
|
};
|
|
72
108
|
|
|
73
109
|
template <typename T>
|
package/src/ffi.cc
CHANGED
|
@@ -247,8 +247,13 @@ 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
|
+
if (info.Length() < (uint32_t)func->parameters.len) {
|
|
251
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
|
|
252
|
+
return env.Null();
|
|
253
|
+
}
|
|
254
|
+
|
|
250
255
|
CallData call(env, instance, func);
|
|
251
|
-
return call.
|
|
256
|
+
return call.Run(info);
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
@@ -266,6 +271,10 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
266
271
|
func.parameters.Leak();
|
|
267
272
|
};
|
|
268
273
|
|
|
274
|
+
if (info.Length() < (uint32_t)func.parameters.len) {
|
|
275
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 arguments or more, got %2", func.parameters.len, info.Length());
|
|
276
|
+
return env.Null();
|
|
277
|
+
}
|
|
269
278
|
if ((info.Length() - func.parameters.len) % 2) {
|
|
270
279
|
ThrowError<Napi::Error>(env, "Missing value argument for variadic call");
|
|
271
280
|
return env.Null();
|
|
@@ -301,7 +310,7 @@ static Napi::Value TranslateVariadicCall(const Napi::CallbackInfo &info)
|
|
|
301
310
|
return env.Null();
|
|
302
311
|
|
|
303
312
|
CallData call(env, instance, &func);
|
|
304
|
-
return call.
|
|
313
|
+
return call.Run(info);
|
|
305
314
|
}
|
|
306
315
|
|
|
307
316
|
static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value ret,
|
package/src/ffi.hh
CHANGED
|
@@ -26,7 +26,6 @@ extern const int TypeInfoMarker;
|
|
|
26
26
|
|
|
27
27
|
enum class PrimitiveKind {
|
|
28
28
|
Void,
|
|
29
|
-
|
|
30
29
|
Bool,
|
|
31
30
|
Int8,
|
|
32
31
|
UInt8,
|
|
@@ -36,18 +35,15 @@ enum class PrimitiveKind {
|
|
|
36
35
|
UInt32,
|
|
37
36
|
Int64,
|
|
38
37
|
UInt64,
|
|
39
|
-
Float32,
|
|
40
|
-
Float64,
|
|
41
38
|
String,
|
|
42
39
|
String16,
|
|
43
|
-
|
|
44
40
|
Pointer,
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
Record,
|
|
42
|
+
Float32,
|
|
43
|
+
Float64
|
|
47
44
|
};
|
|
48
45
|
static const char *const PrimitiveKindNames[] = {
|
|
49
46
|
"Void",
|
|
50
|
-
|
|
51
47
|
"Bool",
|
|
52
48
|
"Int8",
|
|
53
49
|
"UInt8",
|
|
@@ -57,14 +53,12 @@ static const char *const PrimitiveKindNames[] = {
|
|
|
57
53
|
"UInt32",
|
|
58
54
|
"Int64",
|
|
59
55
|
"UInt64",
|
|
60
|
-
"Float32",
|
|
61
|
-
"Float64",
|
|
62
56
|
"String",
|
|
63
57
|
"String16",
|
|
64
|
-
|
|
65
58
|
"Pointer",
|
|
66
|
-
|
|
67
|
-
"
|
|
59
|
+
"Record",
|
|
60
|
+
"Float32",
|
|
61
|
+
"Float64"
|
|
68
62
|
};
|
|
69
63
|
|
|
70
64
|
struct TypeInfo;
|