koffi 3.0.1 → 3.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/CHANGELOG.md +11 -0
- package/cnoke.cjs +7 -7
- package/doc/benchmarks.md +1 -1
- package/doc/contribute.md +1 -1
- package/index.d.ts +356 -308
- package/package.json +16 -16
- package/src/koffi/CMakeLists.txt +1 -0
- package/src/koffi/index.cjs +30 -116
- package/src/koffi/index.js +27 -101
- package/src/koffi/indirect.cjs +29 -115
- package/src/koffi/indirect.js +28 -28
- package/src/koffi/src/abi/arm64.cc +1 -0
- package/src/koffi/src/abi/riscv64.cc +1 -0
- package/src/koffi/src/abi/x64sysv.cc +1 -0
- package/src/koffi/src/abi/x64win.cc +1 -0
- package/src/koffi/src/abi/x86.cc +1 -0
- package/src/koffi/src/call.cc +208 -97
- package/src/koffi/src/call.hh +2 -1
- package/src/koffi/src/ffi.cc +347 -237
- package/src/koffi/src/ffi.hh +37 -1
- package/src/koffi/src/parser.cc +3 -1
- package/src/koffi/src/static.cjs +122 -0
- package/src/koffi/src/static.js +122 -0
- package/src/koffi/src/type.cc +715 -0
- package/src/koffi/src/type.hh +71 -0
- package/src/koffi/src/util.cc +56 -1041
- package/src/koffi/src/util.hh +80 -119
- package/src/koffi/src/uv.cc +16 -10
- package/src/koffi/src/uv.hh +2 -1
- package/indirect.d.ts +0 -322
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 Niels Martignène <niels.martignene@protonmail.com>
|
|
3
|
+
|
|
4
|
+
#include "lib/native/base/base.hh"
|
|
5
|
+
#include "ffi.hh"
|
|
6
|
+
#include "type.hh"
|
|
7
|
+
#include "util.hh"
|
|
8
|
+
|
|
9
|
+
#include <napi.h>
|
|
10
|
+
|
|
11
|
+
namespace K {
|
|
12
|
+
|
|
13
|
+
#if !defined(EXTERNAL_TYPES)
|
|
14
|
+
|
|
15
|
+
Napi::Function TypeObject::InitClass(InstanceData *instance)
|
|
16
|
+
{
|
|
17
|
+
Napi::Env env = instance->env;
|
|
18
|
+
Napi::Function constructor = DefineClass(env, "TypeObject", {}, instance);
|
|
19
|
+
|
|
20
|
+
return constructor;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
TypeObject::TypeObject(const Napi::CallbackInfo &info)
|
|
24
|
+
: Napi::ObjectWrap<TypeObject>(info)
|
|
25
|
+
{
|
|
26
|
+
Napi::Env env = info.Env();
|
|
27
|
+
|
|
28
|
+
if (info.Length() < 1 || !info[0u].IsExternal()) [[unlikely]] {
|
|
29
|
+
ThrowError<Napi::Error>(env, "Type objects cannot be constructed manually");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Napi::External<TypeInfo> external = info[0u].As<Napi::External<TypeInfo>>();
|
|
34
|
+
type = external.Data();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void TypeObject::Finalize(Napi::BasicEnv env)
|
|
38
|
+
{
|
|
39
|
+
DeleteReferenceSafe(env, *this);
|
|
40
|
+
SuppressDestruct();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
static inline bool IsIdentifierStart(char c)
|
|
46
|
+
{
|
|
47
|
+
return IsAsciiAlpha(c) || c == '_';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static inline bool IsIdentifierChar(char c)
|
|
51
|
+
{
|
|
52
|
+
return IsAsciiAlphaOrDigit(c) || c == '_';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static inline Span<const char> SplitIdentifier(Span<const char> str)
|
|
56
|
+
{
|
|
57
|
+
Size offset = 0;
|
|
58
|
+
|
|
59
|
+
if (str.len && IsIdentifierStart(str[0])) {
|
|
60
|
+
offset++;
|
|
61
|
+
|
|
62
|
+
while (offset < str.len && IsIdentifierChar(str[offset])) {
|
|
63
|
+
offset++;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Span<const char> token = str.Take(0, offset);
|
|
68
|
+
return token;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
int ResolveDirections(Span<const char> str)
|
|
72
|
+
{
|
|
73
|
+
if (str == "_In_") {
|
|
74
|
+
return 1;
|
|
75
|
+
} else if (str == "_Out_") {
|
|
76
|
+
return 2;
|
|
77
|
+
} else if (str == "_Inout_") {
|
|
78
|
+
return 3;
|
|
79
|
+
} else {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const TypeInfo *ResolveType(InstanceData *instance, napi_value value, int *out_directions)
|
|
85
|
+
{
|
|
86
|
+
Napi::Env env = instance->env;
|
|
87
|
+
|
|
88
|
+
// String?
|
|
89
|
+
{
|
|
90
|
+
char str[1024];
|
|
91
|
+
size_t len;
|
|
92
|
+
|
|
93
|
+
napi_status status = napi_get_value_string_utf8(env, value, str, K_SIZE(str), &len);
|
|
94
|
+
|
|
95
|
+
if (status == napi_ok) {
|
|
96
|
+
Span<const char> remain = MakeSpan(str, (Size)len);
|
|
97
|
+
|
|
98
|
+
// Quick path for known types (int, float *, etc.)
|
|
99
|
+
const TypeInfo *type = instance->types_map.FindValue(remain.ptr, nullptr);
|
|
100
|
+
|
|
101
|
+
if (!type) {
|
|
102
|
+
if (out_directions) {
|
|
103
|
+
Span<const char> prefix = SplitIdentifier(remain);
|
|
104
|
+
int directions = ResolveDirections(prefix);
|
|
105
|
+
|
|
106
|
+
if (directions) {
|
|
107
|
+
remain = remain.Take(prefix.len, remain.len - prefix.len);
|
|
108
|
+
remain = TrimStrLeft(remain);
|
|
109
|
+
|
|
110
|
+
*out_directions = directions;
|
|
111
|
+
} else {
|
|
112
|
+
*out_directions = 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type = ResolveType(instance, remain.ptr);
|
|
117
|
+
|
|
118
|
+
if (!type) {
|
|
119
|
+
if (!env.IsExceptionPending()) {
|
|
120
|
+
ThrowError<Napi::TypeError>(env, "Unknown or invalid type name '%1'", str);
|
|
121
|
+
}
|
|
122
|
+
return nullptr;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Cache for quick future access
|
|
126
|
+
bool inserted;
|
|
127
|
+
auto bucket = instance->types_map.InsertOrGetDefault(remain.ptr, &inserted);
|
|
128
|
+
|
|
129
|
+
if (inserted) {
|
|
130
|
+
bucket->key = DuplicateString(remain, &instance->str_alloc).ptr;
|
|
131
|
+
bucket->value = type;
|
|
132
|
+
}
|
|
133
|
+
} else if (out_directions) {
|
|
134
|
+
*out_directions = 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return type;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
napi_valuetype kind = GetKindOf(env, value);
|
|
142
|
+
|
|
143
|
+
if (kind == napi_external && CheckValueTag(env, value, &DirectionMarker)) {
|
|
144
|
+
Napi::External<TypeInfo> external = Napi::External<TypeInfo>(env, value);
|
|
145
|
+
const TypeInfo *raw = external.Data();
|
|
146
|
+
|
|
147
|
+
const TypeInfo *type = AlignDown(raw, 4);
|
|
148
|
+
K_ASSERT(type);
|
|
149
|
+
|
|
150
|
+
if (out_directions) {
|
|
151
|
+
Size delta = (uint8_t *)raw - (uint8_t *)type;
|
|
152
|
+
*out_directions = 1 + (int)delta;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return type;
|
|
156
|
+
#if defined(EXTERNAL_TYPES)
|
|
157
|
+
} else if (kind == napi_external && CheckValueTag(env, value, &TypeObjectMarker)) {
|
|
158
|
+
Napi::External<TypeInfo> external = Napi::External<TypeInfo>(env, value);
|
|
159
|
+
const TypeInfo *type = external.Data();
|
|
160
|
+
|
|
161
|
+
if (out_directions) {
|
|
162
|
+
*out_directions = 1;
|
|
163
|
+
}
|
|
164
|
+
return type;
|
|
165
|
+
#else
|
|
166
|
+
} else if (kind == napi_object && CheckValueTag(env, value, &TypeObjectMarker)) {
|
|
167
|
+
TypeObject *defn = nullptr;
|
|
168
|
+
NAPI_OK(napi_unwrap(env, value, (void **)&defn));
|
|
169
|
+
|
|
170
|
+
if (out_directions) {
|
|
171
|
+
*out_directions = 1;
|
|
172
|
+
}
|
|
173
|
+
return defn->GetType();
|
|
174
|
+
#endif
|
|
175
|
+
} else {
|
|
176
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value as type specifier, expected string or type", GetValueType(instance, value));
|
|
177
|
+
return nullptr;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const TypeInfo *ResolveType(InstanceData *instance, Span<const char> str)
|
|
182
|
+
{
|
|
183
|
+
Napi::Env env = instance->env;
|
|
184
|
+
|
|
185
|
+
// Each item can be > 0 for array or 0 for a pointer
|
|
186
|
+
LocalArray<Size, 8> arrays;
|
|
187
|
+
uint8_t disposables = 0;
|
|
188
|
+
|
|
189
|
+
Span<const char> name;
|
|
190
|
+
Span<const char> after;
|
|
191
|
+
{
|
|
192
|
+
Span<const char> remain = str;
|
|
193
|
+
|
|
194
|
+
// Skip initial const qualifiers
|
|
195
|
+
remain = TrimStrLeft(remain);
|
|
196
|
+
while (SplitIdentifier(remain) == "const") {
|
|
197
|
+
remain = remain.Take(6, remain.len - 6);
|
|
198
|
+
remain = TrimStrLeft(remain);
|
|
199
|
+
}
|
|
200
|
+
remain = TrimStrLeft(remain);
|
|
201
|
+
|
|
202
|
+
after = remain;
|
|
203
|
+
|
|
204
|
+
// Consume one or more identifiers (e.g. unsigned int)
|
|
205
|
+
for (;;) {
|
|
206
|
+
after = TrimStrLeft(after);
|
|
207
|
+
|
|
208
|
+
Span<const char> token = SplitIdentifier(after);
|
|
209
|
+
if (!token.len)
|
|
210
|
+
break;
|
|
211
|
+
after = after.Take(token.len, after.len - token.len);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
name = TrimStr(MakeSpan(remain.ptr, after.ptr - remain.ptr));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Consume type indirections (pointer, array, etc.)
|
|
218
|
+
while (after.len) {
|
|
219
|
+
if (after[0] == '*') {
|
|
220
|
+
after = after.Take(1, after.len - 1);
|
|
221
|
+
|
|
222
|
+
if (!arrays.Available()) [[unlikely]] {
|
|
223
|
+
ThrowError<Napi::Error>(env, "Too many type indirections");
|
|
224
|
+
return nullptr;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
arrays.Append(0);
|
|
228
|
+
} else if (after[0] == '!') {
|
|
229
|
+
after = after.Take(1, after.len - 1);
|
|
230
|
+
disposables |= (1u << arrays.len);
|
|
231
|
+
} else if (after[0] == '[') {
|
|
232
|
+
after = after.Take(1, after.len - 1);
|
|
233
|
+
|
|
234
|
+
Size len = 0;
|
|
235
|
+
|
|
236
|
+
after = TrimStrLeft(after);
|
|
237
|
+
if (!ParseInt(after, &len, 0, &after) || len < 0) [[unlikely]] {
|
|
238
|
+
ThrowError<Napi::Error>(env, "Invalid array length");
|
|
239
|
+
return nullptr;
|
|
240
|
+
}
|
|
241
|
+
after = TrimStrLeft(after);
|
|
242
|
+
if (!after.len || after[0] != ']') [[unlikely]] {
|
|
243
|
+
ThrowError<Napi::Error>(env, "Expected ']' after array length");
|
|
244
|
+
return nullptr;
|
|
245
|
+
}
|
|
246
|
+
after = after.Take(1, after.len - 1);
|
|
247
|
+
|
|
248
|
+
if (!arrays.Available()) [[unlikely]] {
|
|
249
|
+
ThrowError<Napi::Error>(env, "Too many type indirections");
|
|
250
|
+
return nullptr;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
arrays.Append(len);
|
|
254
|
+
} else if (SplitIdentifier(after) == "const") {
|
|
255
|
+
after = after.Take(6, after.len - 6);
|
|
256
|
+
} else {
|
|
257
|
+
after = TrimStrRight(after);
|
|
258
|
+
|
|
259
|
+
if (after.len) [[unlikely]] {
|
|
260
|
+
ThrowError<Napi::Error>(env, "Unexpected character '%1' in type specifier", after[0]);
|
|
261
|
+
return nullptr;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
after = TrimStrLeft(after);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const TypeInfo *type = instance->types_map.FindValue(name, nullptr);
|
|
271
|
+
|
|
272
|
+
if (!type) {
|
|
273
|
+
// Try with cleaned up spaces
|
|
274
|
+
if (name.len < 256) {
|
|
275
|
+
LocalArray<char, 256> buf;
|
|
276
|
+
for (Size i = 0; i < name.len; i++) {
|
|
277
|
+
char c = name[i];
|
|
278
|
+
|
|
279
|
+
if (IsAsciiWhite(c)) {
|
|
280
|
+
buf.Append(' ');
|
|
281
|
+
while (++i < name.len && IsAsciiWhite(name[i]));
|
|
282
|
+
i--;
|
|
283
|
+
} else {
|
|
284
|
+
buf.Append(c);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
type = instance->types_map.FindValue(buf, nullptr);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!type)
|
|
292
|
+
return nullptr;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (int i = 0;; i++) {
|
|
296
|
+
if (disposables & (1u << i)) {
|
|
297
|
+
if (type->primitive != PrimitiveKind::Pointer &&
|
|
298
|
+
type->primitive != PrimitiveKind::String &&
|
|
299
|
+
type->primitive != PrimitiveKind::String16 &&
|
|
300
|
+
type->primitive != PrimitiveKind::String32) [[unlikely]] {
|
|
301
|
+
ThrowError<Napi::Error>(env, "Cannot create disposable type for non-pointer");
|
|
302
|
+
return nullptr;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
TypeInfo *copy = instance->types.AppendDefault();
|
|
306
|
+
|
|
307
|
+
memcpy((void *)copy, (const void *)type, K_SIZE(*type));
|
|
308
|
+
copy->name = Fmt(&instance->str_alloc, "<anonymous_%1>", instance->types.count).ptr;
|
|
309
|
+
copy->members.allocator = GetNullAllocator();
|
|
310
|
+
copy->members.allocator = GetNullAllocator();
|
|
311
|
+
memset((void *)©->defn, 0, K_SIZE(copy->defn));
|
|
312
|
+
|
|
313
|
+
static_assert(!std::is_polymorphic_v<Napi::ObjectReference>);
|
|
314
|
+
|
|
315
|
+
copy->dispose = [](Napi::Env env, const TypeInfo *, const void *ptr) {
|
|
316
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
317
|
+
|
|
318
|
+
free((void *)ptr);
|
|
319
|
+
instance->stats.disposed++;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
type = copy;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (i >= arrays.len)
|
|
326
|
+
break;
|
|
327
|
+
Size len = arrays[i];
|
|
328
|
+
|
|
329
|
+
if (len > 0) {
|
|
330
|
+
if (type->primitive == PrimitiveKind::Void) [[unlikely]] {
|
|
331
|
+
ThrowError<Napi::TypeError>(env, "Cannot make array of empty or incomplete type");
|
|
332
|
+
return nullptr;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (len > instance->config.max_type_size / type->size) {
|
|
336
|
+
ThrowError<Napi::TypeError>(env, "Array length is too high (max = %1)", instance->config.max_type_size / type->size);
|
|
337
|
+
return nullptr;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
type = MakeArrayType(instance, type, len);
|
|
341
|
+
K_ASSERT(type);
|
|
342
|
+
} else {
|
|
343
|
+
K_ASSERT(!len);
|
|
344
|
+
|
|
345
|
+
type = MakePointerType(instance, type);
|
|
346
|
+
K_ASSERT(type);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return type;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
TypeInfo *MakePointerType(InstanceData *instance, const TypeInfo *ref, int count)
|
|
354
|
+
{
|
|
355
|
+
K_ASSERT(count >= 1);
|
|
356
|
+
|
|
357
|
+
for (int i = 0; i < count; i++) {
|
|
358
|
+
char name_buf[256];
|
|
359
|
+
Fmt(name_buf, "%1%2*", ref->name, EndsWith(ref->name, "*") ? "" : " ");
|
|
360
|
+
|
|
361
|
+
bool inserted;
|
|
362
|
+
auto bucket = instance->types_map.InsertOrGetDefault(name_buf, &inserted);
|
|
363
|
+
|
|
364
|
+
if (inserted) {
|
|
365
|
+
TypeInfo *type = instance->types.AppendDefault();
|
|
366
|
+
|
|
367
|
+
type->name = DuplicateString(name_buf, &instance->str_alloc).ptr;
|
|
368
|
+
|
|
369
|
+
if (ref->primitive != PrimitiveKind::Prototype) {
|
|
370
|
+
type->primitive = PrimitiveKind::Pointer;
|
|
371
|
+
type->size = K_SIZE(void *);
|
|
372
|
+
type->align = K_SIZE(void *);
|
|
373
|
+
type->ref.type = ref;
|
|
374
|
+
type->ref.stride = ref->size;
|
|
375
|
+
type->hint = (ref->flags & (int)TypeFlag::HasTypedArray) ? ArrayHint::Typed : ArrayHint::Array;
|
|
376
|
+
} else {
|
|
377
|
+
type->primitive = PrimitiveKind::Callback;
|
|
378
|
+
type->size = K_SIZE(void *);
|
|
379
|
+
type->align = K_SIZE(void *);
|
|
380
|
+
type->ref.type = instance->void_type; // Dummy
|
|
381
|
+
type->proto = ref->proto;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
bucket->key = type->name;
|
|
385
|
+
bucket->value = type;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
ref = bucket->value;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return (TypeInfo *)ref;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
static TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint, bool insert)
|
|
395
|
+
{
|
|
396
|
+
K_ASSERT(len >= 0);
|
|
397
|
+
K_ASSERT(len <= instance->config.max_type_size / ref->size);
|
|
398
|
+
|
|
399
|
+
TypeInfo *type = instance->types.AppendDefault();
|
|
400
|
+
|
|
401
|
+
type->name = Fmt(&instance->str_alloc, "%1[%2]", ref->name, len).ptr;
|
|
402
|
+
|
|
403
|
+
type->primitive = PrimitiveKind::Array;
|
|
404
|
+
type->align = ref->align;
|
|
405
|
+
type->size = (int32_t)(len * ref->size);
|
|
406
|
+
type->ref.type = ref;
|
|
407
|
+
type->ref.stride = ref->size;
|
|
408
|
+
type->hint = hint;
|
|
409
|
+
|
|
410
|
+
if (insert) {
|
|
411
|
+
bool inserted;
|
|
412
|
+
type = (TypeInfo *)*instance->types_map.InsertOrGet(type->name, type, &inserted);
|
|
413
|
+
instance->types.RemoveLast(!inserted);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return type;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len)
|
|
420
|
+
{
|
|
421
|
+
ArrayHint hint = {};
|
|
422
|
+
|
|
423
|
+
if (ref->flags & (int)TypeFlag::IsCharLike) {
|
|
424
|
+
hint = ArrayHint::String;
|
|
425
|
+
} else if (ref->flags & (int)TypeFlag::HasTypedArray) {
|
|
426
|
+
hint = ArrayHint::Typed;
|
|
427
|
+
} else {
|
|
428
|
+
hint = ArrayHint::Array;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return MakeArrayType(instance, ref, len, hint, true);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
TypeInfo *MakeArrayType(InstanceData *instance, const TypeInfo *ref, Size len, ArrayHint hint)
|
|
435
|
+
{
|
|
436
|
+
return MakeArrayType(instance, ref, len, hint, false);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
napi_value WrapType(Napi::Env env, const TypeInfo *type, bool freeze)
|
|
440
|
+
{
|
|
441
|
+
if (type->defn.IsEmpty()) {
|
|
442
|
+
#if defined(EXTERNAL_TYPES)
|
|
443
|
+
Napi::Object defn = Napi::Object::New(env);
|
|
444
|
+
#else
|
|
445
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
446
|
+
|
|
447
|
+
Napi::External<TypeInfo> external = Napi::External<TypeInfo>::New(env, (TypeInfo *)type);
|
|
448
|
+
Napi::Object defn = instance->construct_type.New({ external });
|
|
449
|
+
SetValueTag(env, defn, &TypeObjectMarker);
|
|
450
|
+
#endif
|
|
451
|
+
|
|
452
|
+
defn.Set("name", Napi::String::New(env, type->name));
|
|
453
|
+
defn.Set("primitive", PrimitiveKindNames[(int)type->primitive]);
|
|
454
|
+
defn.Set("size", Napi::Number::New(env, (double)type->size));
|
|
455
|
+
defn.Set("alignment", Napi::Number::New(env, (double)type->align));
|
|
456
|
+
defn.Set("disposable", Napi::Boolean::New(env, !!type->dispose));
|
|
457
|
+
|
|
458
|
+
// Assign before to avoid possible recursion crash
|
|
459
|
+
type->defn = Napi::Persistent(defn);
|
|
460
|
+
|
|
461
|
+
switch (type->primitive) {
|
|
462
|
+
case PrimitiveKind::Void:
|
|
463
|
+
case PrimitiveKind::Bool:
|
|
464
|
+
case PrimitiveKind::Int8:
|
|
465
|
+
case PrimitiveKind::UInt8:
|
|
466
|
+
case PrimitiveKind::Int16:
|
|
467
|
+
case PrimitiveKind::Int16S:
|
|
468
|
+
case PrimitiveKind::UInt16:
|
|
469
|
+
case PrimitiveKind::UInt16S:
|
|
470
|
+
case PrimitiveKind::Int32:
|
|
471
|
+
case PrimitiveKind::Int32S:
|
|
472
|
+
case PrimitiveKind::UInt32:
|
|
473
|
+
case PrimitiveKind::UInt32S:
|
|
474
|
+
case PrimitiveKind::Int64:
|
|
475
|
+
case PrimitiveKind::Int64S:
|
|
476
|
+
case PrimitiveKind::UInt64:
|
|
477
|
+
case PrimitiveKind::UInt64S:
|
|
478
|
+
case PrimitiveKind::String:
|
|
479
|
+
case PrimitiveKind::String16:
|
|
480
|
+
case PrimitiveKind::String32:
|
|
481
|
+
case PrimitiveKind::Float32:
|
|
482
|
+
case PrimitiveKind::Float64: {} break;
|
|
483
|
+
|
|
484
|
+
case PrimitiveKind::Array: {
|
|
485
|
+
uint32_t len = type->size / type->ref.type->size;
|
|
486
|
+
defn.Set("length", Napi::Number::New(env, (double)len));
|
|
487
|
+
defn.Set("hint", ArrayHintNames[(int)type->hint]);
|
|
488
|
+
} [[fallthrough]];
|
|
489
|
+
case PrimitiveKind::Pointer: {
|
|
490
|
+
napi_value value = WrapType(env, type->ref.type);
|
|
491
|
+
defn.Set("ref", value);
|
|
492
|
+
} break;
|
|
493
|
+
case PrimitiveKind::Record:
|
|
494
|
+
case PrimitiveKind::Union: {
|
|
495
|
+
Napi::Object members = Napi::Object::New(env);
|
|
496
|
+
|
|
497
|
+
for (const RecordMember &member: type->members) {
|
|
498
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
499
|
+
|
|
500
|
+
obj.Set("name", member.name);
|
|
501
|
+
obj.Set("type", WrapType(env, member.type));
|
|
502
|
+
obj.Set("offset", member.offset);
|
|
503
|
+
|
|
504
|
+
members.Set(member.name, obj);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
members.Freeze();
|
|
508
|
+
defn.Set("members", members);
|
|
509
|
+
} break;
|
|
510
|
+
|
|
511
|
+
case PrimitiveKind::Prototype:
|
|
512
|
+
case PrimitiveKind::Callback: {
|
|
513
|
+
napi_value meta = DescribeFunction(env, type->proto);
|
|
514
|
+
defn.Set("proto", meta);
|
|
515
|
+
} break;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (freeze) {
|
|
519
|
+
defn.Freeze();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
#if defined(EXTERNAL_TYPES)
|
|
524
|
+
Napi::External<TypeInfo> external = Napi::External<TypeInfo>::New(env, (TypeInfo *)type);
|
|
525
|
+
SetValueTag(env, external, &TypeObjectMarker);
|
|
526
|
+
|
|
527
|
+
return external;
|
|
528
|
+
#else
|
|
529
|
+
return type->defn.Value();
|
|
530
|
+
#endif
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const TypeInfo *ReshapeType(InstanceData *instance, const TypeInfo *type, int32_t stride, uint16_t flags)
|
|
534
|
+
{
|
|
535
|
+
K_ASSERT(!type->defn.IsEmpty());
|
|
536
|
+
|
|
537
|
+
if (!type->reshaped) {
|
|
538
|
+
TypeInfo *reshaped = nullptr;
|
|
539
|
+
|
|
540
|
+
switch (type->primitive) {
|
|
541
|
+
case PrimitiveKind::Record: {
|
|
542
|
+
reshaped = instance->types.AppendDefault();
|
|
543
|
+
|
|
544
|
+
memcpy((void *)reshaped, (const void *)type, K_SIZE(*type));
|
|
545
|
+
memset((void *)&reshaped->members, 0, K_SIZE(reshaped->members));
|
|
546
|
+
reshaped->members.Reserve(type->members.len);
|
|
547
|
+
reshaped->size = 0;
|
|
548
|
+
reshaped->flags |= flags;
|
|
549
|
+
memset((void *)&reshaped->defn, 0, K_SIZE(reshaped->defn));
|
|
550
|
+
|
|
551
|
+
Napi::Object defn = type->defn.Value();
|
|
552
|
+
reshaped->defn = Napi::Persistent(defn);
|
|
553
|
+
|
|
554
|
+
for (RecordMember member: type->members) {
|
|
555
|
+
member.offset = reshaped->size;
|
|
556
|
+
member.type = ReshapeType(instance, member.type, stride, flags);
|
|
557
|
+
|
|
558
|
+
reshaped->members.Append(member);
|
|
559
|
+
reshaped->size += AlignLen(member.type->size, stride);
|
|
560
|
+
}
|
|
561
|
+
} break;
|
|
562
|
+
|
|
563
|
+
case PrimitiveKind::Array: {
|
|
564
|
+
reshaped = instance->types.AppendDefault();
|
|
565
|
+
|
|
566
|
+
memcpy((void *)reshaped, (const void *)type, K_SIZE(*type));
|
|
567
|
+
reshaped->ref.stride = stride;
|
|
568
|
+
reshaped->size = (type->size / type->ref.stride) * stride;
|
|
569
|
+
memset((void *)&reshaped->defn, 0, K_SIZE(reshaped->defn));
|
|
570
|
+
reshaped->flags |= flags;
|
|
571
|
+
|
|
572
|
+
Napi::Object defn = type->defn.Value();
|
|
573
|
+
reshaped->defn = Napi::Persistent(defn);
|
|
574
|
+
} break;
|
|
575
|
+
|
|
576
|
+
default: { reshaped = (TypeInfo *)type; } break;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
type->reshaped = reshaped;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return type->reshaped;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
bool CanPassType(const TypeInfo *type, int directions)
|
|
586
|
+
{
|
|
587
|
+
if (type->countedby)
|
|
588
|
+
return false;
|
|
589
|
+
|
|
590
|
+
if (directions & 2) {
|
|
591
|
+
if (type->primitive == PrimitiveKind::Pointer)
|
|
592
|
+
return true;
|
|
593
|
+
if (type->primitive == PrimitiveKind::String)
|
|
594
|
+
return true;
|
|
595
|
+
if (type->primitive == PrimitiveKind::String16)
|
|
596
|
+
return true;
|
|
597
|
+
if (type->primitive == PrimitiveKind::String32)
|
|
598
|
+
return true;
|
|
599
|
+
|
|
600
|
+
return false;
|
|
601
|
+
} else {
|
|
602
|
+
if (type->primitive == PrimitiveKind::Void)
|
|
603
|
+
return false;
|
|
604
|
+
if (type->primitive == PrimitiveKind::Array)
|
|
605
|
+
return false;
|
|
606
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
607
|
+
return false;
|
|
608
|
+
if (type->primitive == PrimitiveKind::Callback && type->proto->variadic)
|
|
609
|
+
return false;
|
|
610
|
+
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
bool CanReturnType(const TypeInfo *type)
|
|
616
|
+
{
|
|
617
|
+
if (type->countedby)
|
|
618
|
+
return false;
|
|
619
|
+
|
|
620
|
+
if (type->primitive == PrimitiveKind::Void && !TestStr(type->name, "void"))
|
|
621
|
+
return false;
|
|
622
|
+
if (type->primitive == PrimitiveKind::Array)
|
|
623
|
+
return false;
|
|
624
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
625
|
+
return false;
|
|
626
|
+
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
bool CanStoreType(const TypeInfo *type)
|
|
631
|
+
{
|
|
632
|
+
if (type->primitive == PrimitiveKind::Void)
|
|
633
|
+
return false;
|
|
634
|
+
if (type->primitive == PrimitiveKind::Prototype)
|
|
635
|
+
return false;
|
|
636
|
+
if (type->primitive == PrimitiveKind::Callback && type->proto->variadic)
|
|
637
|
+
return false;
|
|
638
|
+
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const char *GetValueType(const InstanceData *instance, napi_value value)
|
|
643
|
+
{
|
|
644
|
+
Napi::Env env = instance->env;
|
|
645
|
+
napi_valuetype kind = GetKindOf(env, value);
|
|
646
|
+
|
|
647
|
+
if (kind == napi_external) {
|
|
648
|
+
if (CheckValueTag(env, value, &CastMarker)) {
|
|
649
|
+
Napi::External<ValueCast> external = Napi::External<ValueCast>(env, value);
|
|
650
|
+
ValueCast *cast = external.Data();
|
|
651
|
+
|
|
652
|
+
return cast->type->name;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (CheckValueTag(env, value, &LibraryHandleMarker))
|
|
656
|
+
return "LibraryHandle";
|
|
657
|
+
if (CheckValueTag(env, value, &TypeObjectMarker))
|
|
658
|
+
return "TypeObject";
|
|
659
|
+
|
|
660
|
+
if (CheckValueTag(env, value, &UnionValueMarker)) {
|
|
661
|
+
UnionValue *u = nullptr;
|
|
662
|
+
napi_unwrap(env, value, (void **)&u);
|
|
663
|
+
|
|
664
|
+
return u->GetType()->name;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
for (const TypeInfo &type: instance->types) {
|
|
668
|
+
if (type.ref.type && CheckValueTag(env, value, type.ref.type))
|
|
669
|
+
return type.name;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (IsArray(env, value)) {
|
|
674
|
+
return "Array";
|
|
675
|
+
} else if (IsTypedArray(env, value)) {
|
|
676
|
+
Napi::TypedArray array = Napi::TypedArray(env, value);
|
|
677
|
+
|
|
678
|
+
switch (array.TypedArrayType()) {
|
|
679
|
+
case napi_int8_array: return "Int8Array";
|
|
680
|
+
case napi_uint8_array: return "Uint8Array";
|
|
681
|
+
case napi_uint8_clamped_array: return "Uint8ClampedArray";
|
|
682
|
+
case napi_int16_array: return "Int16Array";
|
|
683
|
+
case napi_uint16_array: return "Uint16Array";
|
|
684
|
+
case napi_int32_array: return "Int32Array";
|
|
685
|
+
case napi_uint32_array: return "Uint32Array";
|
|
686
|
+
case napi_float16_array: return "Float16Array";
|
|
687
|
+
case napi_float32_array: return "Float32Array";
|
|
688
|
+
case napi_float64_array: return "Float64Array";
|
|
689
|
+
case napi_bigint64_array: return "BigInt64Array";
|
|
690
|
+
case napi_biguint64_array: return "BigUint64Array";
|
|
691
|
+
}
|
|
692
|
+
} else if (IsArrayBuffer(env, value)) {
|
|
693
|
+
return "ArrayBuffer";
|
|
694
|
+
} else if (IsBuffer(env, value)) {
|
|
695
|
+
return "Buffer";
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
switch (kind) {
|
|
699
|
+
case napi_undefined: return "Undefined";
|
|
700
|
+
case napi_null: return "Null";
|
|
701
|
+
case napi_boolean: return "Boolean";
|
|
702
|
+
case napi_number: return "Number";
|
|
703
|
+
case napi_string: return "String";
|
|
704
|
+
case napi_symbol: return "Symbol";
|
|
705
|
+
case napi_object: return "Object";
|
|
706
|
+
case napi_function: return "Function";
|
|
707
|
+
case napi_external: return "External";
|
|
708
|
+
case napi_bigint: return "BigInt";
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// This should not be possible, but who knows...
|
|
712
|
+
return "Unknown";
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
}
|