koffi 2.4.1 → 2.5.0-beta.1
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 +12 -2
- package/build/2.5.0-beta.1/koffi_darwin_arm64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_darwin_x64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_freebsd_arm64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_freebsd_ia32/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_freebsd_x64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_linux_arm32hf/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_linux_arm64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_linux_ia32/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_linux_riscv64hf64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_linux_x64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_openbsd_ia32/koffi.node +0 -0
- package/build/{2.4.1 → 2.5.0-beta.1}/koffi_openbsd_x64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_win32_arm64/koffi.node +0 -0
- package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.node +0 -0
- package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_x64/koffi.node +0 -0
- package/build/2.5.0-beta.1/koffi_win32_x64/koffi.pdb +0 -0
- package/doc/callbacks.md +1 -1
- package/doc/conf.py +1 -1
- package/doc/functions.md +73 -4
- package/doc/parameters.md +0 -1
- package/doc/pointers.md +1 -1
- package/package.json +2 -2
- package/src/core/libcc/libcc.cc +58 -54
- package/src/core/libcc/libcc.hh +33 -19
- package/src/koffi/src/abi_x86.cc +31 -4
- package/src/koffi/src/call.cc +37 -22
- package/src/koffi/src/ffi.cc +253 -310
- package/src/koffi/src/ffi.hh +3 -1
- package/src/koffi/src/parser.cc +1 -1
- package/src/koffi/src/util.cc +24 -13
- package/src/koffi/src/util.hh +4 -2
- package/build/2.4.1/koffi_darwin_arm64/koffi.node +0 -0
- package/build/2.4.1/koffi_darwin_x64/koffi.node +0 -0
- package/build/2.4.1/koffi_freebsd_arm64/koffi.node +0 -0
- package/build/2.4.1/koffi_freebsd_ia32/koffi.node +0 -0
- package/build/2.4.1/koffi_freebsd_x64/koffi.node +0 -0
- package/build/2.4.1/koffi_linux_arm32hf/koffi.node +0 -0
- package/build/2.4.1/koffi_linux_arm64/koffi.node +0 -0
- package/build/2.4.1/koffi_linux_ia32/koffi.node +0 -0
- package/build/2.4.1/koffi_linux_riscv64hf64/koffi.node +0 -0
- package/build/2.4.1/koffi_linux_x64/koffi.node +0 -0
- package/build/2.4.1/koffi_openbsd_ia32/koffi.node +0 -0
- package/build/2.4.1/koffi_win32_arm64/koffi.node +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_arm64/koffi.exp +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_arm64/koffi.lib +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.exp +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_ia32/koffi.lib +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_x64/koffi.exp +0 -0
- /package/build/{2.4.1 → 2.5.0-beta.1}/koffi_win32_x64/koffi.lib +0 -0
package/src/koffi/src/ffi.cc
CHANGED
|
@@ -895,7 +895,7 @@ static bool ParseClassicFunction(Napi::Env env, Napi::String name, Napi::Value r
|
|
|
895
895
|
if (!param.type)
|
|
896
896
|
return false;
|
|
897
897
|
if (!CanPassType(param.type, param.directions)) {
|
|
898
|
-
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter
|
|
898
|
+
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", param.type->name);
|
|
899
899
|
return false;
|
|
900
900
|
}
|
|
901
901
|
if (func->parameters.len >= MaxParameters) {
|
|
@@ -942,11 +942,6 @@ static Napi::Value CreateFunctionType(const Napi::CallbackInfo &info)
|
|
|
942
942
|
return env.Null();
|
|
943
943
|
}
|
|
944
944
|
|
|
945
|
-
if (func->variadic) {
|
|
946
|
-
LogError("Variadic callbacks are not supported");
|
|
947
|
-
return env.Null();
|
|
948
|
-
}
|
|
949
|
-
|
|
950
945
|
if (!AnalyseFunction(env, instance, func))
|
|
951
946
|
return env.Null();
|
|
952
947
|
|
|
@@ -998,12 +993,15 @@ static Napi::Value CreateTypeAlias(const Napi::CallbackInfo &info)
|
|
|
998
993
|
if (!type)
|
|
999
994
|
return env.Null();
|
|
1000
995
|
|
|
1001
|
-
|
|
1002
|
-
|
|
996
|
+
// Alias the type
|
|
997
|
+
{
|
|
998
|
+
bool inserted;
|
|
999
|
+
instance->types_map.TrySet(alias, type, &inserted);
|
|
1003
1000
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1001
|
+
if (!inserted) {
|
|
1002
|
+
ThrowError<Napi::Error>(env, "Type name '%1' already exists", alias);
|
|
1003
|
+
return env.Null();
|
|
1004
|
+
}
|
|
1007
1005
|
}
|
|
1008
1006
|
|
|
1009
1007
|
return WrapType(env, instance, type);
|
|
@@ -1310,7 +1308,7 @@ static Napi::Value TranslateVariadicCall(const FunctionInfo *func, void *native,
|
|
|
1310
1308
|
if (RG_UNLIKELY(!param.type))
|
|
1311
1309
|
return env.Null();
|
|
1312
1310
|
if (RG_UNLIKELY(!CanPassType(param.type, param.directions))) {
|
|
1313
|
-
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter
|
|
1311
|
+
ThrowError<Napi::TypeError>(env, "Type %1 cannot be used as a parameter", param.type->name);
|
|
1314
1312
|
return env.Null();
|
|
1315
1313
|
}
|
|
1316
1314
|
if (RG_UNLIKELY(copy.parameters.len >= MaxParameters)) {
|
|
@@ -1456,6 +1454,35 @@ Napi::Value TranslateAsyncCall(const Napi::CallbackInfo &info)
|
|
|
1456
1454
|
return TranslateAsyncCall(func, func->native, info);
|
|
1457
1455
|
}
|
|
1458
1456
|
|
|
1457
|
+
extern "C" void RelayCallback(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
|
|
1458
|
+
{
|
|
1459
|
+
if (RG_LIKELY(exec_call)) {
|
|
1460
|
+
exec_call->RelaySafe(idx, own_sp, caller_sp, false, out_reg);
|
|
1461
|
+
} else {
|
|
1462
|
+
// This happens if the callback pointer is called from a different thread
|
|
1463
|
+
// than the one that runs the FFI call (sync or async).
|
|
1464
|
+
|
|
1465
|
+
TrampolineInfo *trampoline = &shared.trampolines[idx];
|
|
1466
|
+
|
|
1467
|
+
Napi::Env env = trampoline->func.Env();
|
|
1468
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1469
|
+
|
|
1470
|
+
InstanceMemory *mem = AllocateMemory(instance, instance->config.async_stack_size, instance->config.async_heap_size);
|
|
1471
|
+
if (RG_UNLIKELY(!mem)) {
|
|
1472
|
+
ThrowError<Napi::Error>(env, "Too many asynchronous calls are running");
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// Avoid triggering the "use callback beyond FFI" check
|
|
1477
|
+
RG_DEFER_C(generation = trampoline->generation) { trampoline->generation = generation; };
|
|
1478
|
+
trampoline->generation = -1;
|
|
1479
|
+
|
|
1480
|
+
// We set dispose_call to true so that the main thread will dispose of CallData itself
|
|
1481
|
+
CallData call(env, instance, mem);
|
|
1482
|
+
call.RelaySafe(idx, own_sp, caller_sp, true, out_reg);
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1459
1486
|
static Napi::Value FindLibraryFunction(const Napi::CallbackInfo &info, CallConvention convention)
|
|
1460
1487
|
{
|
|
1461
1488
|
Napi::Env env = info.Env();
|
|
@@ -1730,6 +1757,116 @@ static Napi::Value UnregisterCallback(const Napi::CallbackInfo &info)
|
|
|
1730
1757
|
return env.Undefined();
|
|
1731
1758
|
}
|
|
1732
1759
|
|
|
1760
|
+
static Napi::Value CastValue(const Napi::CallbackInfo &info)
|
|
1761
|
+
{
|
|
1762
|
+
Napi::Env env = info.Env();
|
|
1763
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1764
|
+
|
|
1765
|
+
if (RG_UNLIKELY(info.Length() < 2)) {
|
|
1766
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
1767
|
+
return env.Null();
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
Napi::Value value = info[0];
|
|
1771
|
+
|
|
1772
|
+
const TypeInfo *type = ResolveType(info[1]);
|
|
1773
|
+
if (RG_UNLIKELY(!type))
|
|
1774
|
+
return env.Null();
|
|
1775
|
+
if (type->primitive != PrimitiveKind::Pointer &&
|
|
1776
|
+
type->primitive != PrimitiveKind::String &&
|
|
1777
|
+
type->primitive != PrimitiveKind::String16) {
|
|
1778
|
+
ThrowError<Napi::TypeError>(env, "Only pointer or string types can be used for casting");
|
|
1779
|
+
return env.Null();
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
ValueCast *cast = new ValueCast;
|
|
1783
|
+
|
|
1784
|
+
cast->ref.Reset(value, 1);
|
|
1785
|
+
cast->type = type;
|
|
1786
|
+
|
|
1787
|
+
Napi::External<ValueCast> external = Napi::External<ValueCast>::New(env, cast, [](Napi::Env, ValueCast *cast) { delete cast; });
|
|
1788
|
+
SetValueTag(instance, external, &CastMarker);
|
|
1789
|
+
|
|
1790
|
+
return external;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
|
|
1794
|
+
{
|
|
1795
|
+
Napi::Env env = info.Env();
|
|
1796
|
+
|
|
1797
|
+
bool has_offset = (info.Length() >= 2 && info[1].IsNumber());
|
|
1798
|
+
bool has_len = (info.Length() >= 3u + has_offset && info[2u + has_offset].IsNumber());
|
|
1799
|
+
|
|
1800
|
+
if (RG_UNLIKELY(info.Length() < 2u + has_offset)) {
|
|
1801
|
+
ThrowError<Napi::TypeError>(env, "Expected %1 to 4 arguments, got %2", 2 + has_offset, info.Length());
|
|
1802
|
+
return env.Null();
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
const TypeInfo *type = ResolveType(info[1u + has_offset]);
|
|
1806
|
+
if (RG_UNLIKELY(!type))
|
|
1807
|
+
return env.Null();
|
|
1808
|
+
|
|
1809
|
+
Napi::Value value = info[0];
|
|
1810
|
+
int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
|
|
1811
|
+
|
|
1812
|
+
if (has_len) {
|
|
1813
|
+
Size len = info[2u + has_offset].As<Napi::Number>();
|
|
1814
|
+
|
|
1815
|
+
Napi::Value ret = Decode(value, offset, type, &len);
|
|
1816
|
+
return ret;
|
|
1817
|
+
} else {
|
|
1818
|
+
Napi::Value ret = Decode(value, offset, type);
|
|
1819
|
+
return ret;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
static Napi::Value GetPointerAddress(const Napi::CallbackInfo &info)
|
|
1824
|
+
{
|
|
1825
|
+
Napi::Env env = info.Env();
|
|
1826
|
+
|
|
1827
|
+
if (info.Length() < 1) {
|
|
1828
|
+
ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
|
|
1829
|
+
return env.Null();
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
void *ptr = nullptr;
|
|
1833
|
+
if (!GetExternalPointer(env, info[0], &ptr))
|
|
1834
|
+
return env.Null();
|
|
1835
|
+
|
|
1836
|
+
uint64_t ptr64 = (uint64_t)(uintptr_t)ptr;
|
|
1837
|
+
Napi::BigInt bigint = Napi::BigInt::New(env, ptr64);
|
|
1838
|
+
|
|
1839
|
+
return bigint;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
static Napi::Value CallPointerSync(const Napi::CallbackInfo &info)
|
|
1843
|
+
{
|
|
1844
|
+
Napi::Env env = info.Env();
|
|
1845
|
+
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1846
|
+
|
|
1847
|
+
if (RG_UNLIKELY(info.Length() < 2)) {
|
|
1848
|
+
ThrowError<Napi::TypeError>(env, "Expected 2 or more arguments, got %1", info.Length());
|
|
1849
|
+
return env.Null();
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
void *ptr = nullptr;
|
|
1853
|
+
if (RG_UNLIKELY(!GetExternalPointer(env, info[0], &ptr)))
|
|
1854
|
+
return env.Null();
|
|
1855
|
+
|
|
1856
|
+
const TypeInfo *type = ResolveType(info[1]);
|
|
1857
|
+
if (RG_UNLIKELY(!type))
|
|
1858
|
+
return env.Null();
|
|
1859
|
+
if (RG_UNLIKELY(type->primitive != PrimitiveKind::Prototype)) {
|
|
1860
|
+
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for type, expected function type", GetValueType(instance, info[1]));
|
|
1861
|
+
return env.Null();
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
const FunctionInfo *proto = type->ref.proto;
|
|
1865
|
+
|
|
1866
|
+
return proto->variadic ? TranslateVariadicCall(proto, ptr, info)
|
|
1867
|
+
: TranslateNormalCall(proto, ptr, info);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1733
1870
|
void LibraryHolder::Unload()
|
|
1734
1871
|
{
|
|
1735
1872
|
#ifdef _WIN32
|
|
@@ -1758,6 +1895,45 @@ void LibraryHolder::Unref() const
|
|
|
1758
1895
|
}
|
|
1759
1896
|
}
|
|
1760
1897
|
|
|
1898
|
+
FunctionInfo::~FunctionInfo()
|
|
1899
|
+
{
|
|
1900
|
+
if (lib) {
|
|
1901
|
+
lib->Unref();
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
const FunctionInfo *FunctionInfo::Ref() const
|
|
1906
|
+
{
|
|
1907
|
+
refcount++;
|
|
1908
|
+
return this;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
void FunctionInfo::Unref() const
|
|
1912
|
+
{
|
|
1913
|
+
if (!--refcount) {
|
|
1914
|
+
delete this;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
InstanceMemory::~InstanceMemory()
|
|
1919
|
+
{
|
|
1920
|
+
#ifdef _WIN32
|
|
1921
|
+
if (stack.ptr) {
|
|
1922
|
+
VirtualFree(stack.ptr, 0, MEM_RELEASE);
|
|
1923
|
+
}
|
|
1924
|
+
if (heap.ptr) {
|
|
1925
|
+
VirtualFree(heap.ptr, 0, MEM_RELEASE);
|
|
1926
|
+
}
|
|
1927
|
+
#else
|
|
1928
|
+
if (stack.ptr) {
|
|
1929
|
+
munmap(stack.ptr, stack.len);
|
|
1930
|
+
}
|
|
1931
|
+
if (heap.ptr) {
|
|
1932
|
+
munmap(heap.ptr, heap.len);
|
|
1933
|
+
}
|
|
1934
|
+
#endif
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1761
1937
|
static void RegisterPrimitiveType(Napi::Env env, Napi::Object map, std::initializer_list<const char *> names,
|
|
1762
1938
|
PrimitiveKind primitive, int32_t size, int16_t align, const char *ref = nullptr)
|
|
1763
1939
|
{
|
|
@@ -1882,50 +2058,13 @@ static Napi::Object InitBaseTypes(Napi::Env env)
|
|
|
1882
2058
|
instance->char_type = instance->types_map.FindValue("char", nullptr);
|
|
1883
2059
|
instance->char16_type = instance->types_map.FindValue("char16", nullptr);
|
|
1884
2060
|
|
|
2061
|
+
instance->active_symbol = Napi::Symbol::New(env, "active");
|
|
2062
|
+
|
|
1885
2063
|
types.Freeze();
|
|
1886
2064
|
|
|
1887
2065
|
return types;
|
|
1888
2066
|
}
|
|
1889
2067
|
|
|
1890
|
-
FunctionInfo::~FunctionInfo()
|
|
1891
|
-
{
|
|
1892
|
-
if (lib) {
|
|
1893
|
-
lib->Unref();
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
const FunctionInfo *FunctionInfo::Ref() const
|
|
1898
|
-
{
|
|
1899
|
-
refcount++;
|
|
1900
|
-
return this;
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
void FunctionInfo::Unref() const
|
|
1904
|
-
{
|
|
1905
|
-
if (!--refcount) {
|
|
1906
|
-
delete this;
|
|
1907
|
-
}
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
InstanceMemory::~InstanceMemory()
|
|
1911
|
-
{
|
|
1912
|
-
#ifdef _WIN32
|
|
1913
|
-
if (stack.ptr) {
|
|
1914
|
-
VirtualFree(stack.ptr, 0, MEM_RELEASE);
|
|
1915
|
-
}
|
|
1916
|
-
if (heap.ptr) {
|
|
1917
|
-
VirtualFree(heap.ptr, 0, MEM_RELEASE);
|
|
1918
|
-
}
|
|
1919
|
-
#else
|
|
1920
|
-
if (stack.ptr) {
|
|
1921
|
-
munmap(stack.ptr, stack.len);
|
|
1922
|
-
}
|
|
1923
|
-
if (heap.ptr) {
|
|
1924
|
-
munmap(heap.ptr, heap.len);
|
|
1925
|
-
}
|
|
1926
|
-
#endif
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
2068
|
static InstanceData *CreateInstance(Napi::Env env)
|
|
1930
2069
|
{
|
|
1931
2070
|
InstanceData *instance = new InstanceData();
|
|
@@ -1933,6 +2072,9 @@ static InstanceData *CreateInstance(Napi::Env env)
|
|
|
1933
2072
|
|
|
1934
2073
|
instance->main_thread_id = std::this_thread::get_id();
|
|
1935
2074
|
|
|
2075
|
+
instance->debug = GetDebugFlag("DUMP_CALLS");
|
|
2076
|
+
FillRandomSafe(&instance->tag_lower, RG_SIZE(instance->tag_lower));
|
|
2077
|
+
|
|
1936
2078
|
if (napi_create_threadsafe_function(env, nullptr, nullptr,
|
|
1937
2079
|
Napi::String::New(env, "Koffi Async Callback Broker"),
|
|
1938
2080
|
0, 1, nullptr, nullptr, nullptr,
|
|
@@ -1953,214 +2095,53 @@ static InstanceData *CreateInstance(Napi::Env env)
|
|
|
1953
2095
|
return instance;
|
|
1954
2096
|
}
|
|
1955
2097
|
|
|
1956
|
-
|
|
1957
|
-
{
|
|
1958
|
-
for (InstanceMemory *mem: memories) {
|
|
1959
|
-
delete mem;
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
// Clean-up leftover trampoline references
|
|
1963
|
-
{
|
|
1964
|
-
std::lock_guard<std::mutex> lock(shared.mutex);
|
|
1965
|
-
|
|
1966
|
-
for (int16_t idx = 0; idx < MaxTrampolines; idx++) {
|
|
1967
|
-
TrampolineInfo *trampoline = &shared.trampolines[idx];
|
|
1968
|
-
|
|
1969
|
-
if (trampoline->instance == this) {
|
|
1970
|
-
trampoline->instance = nullptr;
|
|
1971
|
-
trampoline->func.Reset();
|
|
1972
|
-
trampoline->recv.Reset();
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
if (broker) {
|
|
1978
|
-
napi_release_threadsafe_function(broker, napi_tsfn_abort);
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
static Napi::Value CastValue(const Napi::CallbackInfo &info)
|
|
1983
|
-
{
|
|
1984
|
-
Napi::Env env = info.Env();
|
|
1985
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
1986
|
-
|
|
1987
|
-
if (RG_UNLIKELY(info.Length() < 2)) {
|
|
1988
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 arguments, got %1", info.Length());
|
|
1989
|
-
return env.Null();
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
Napi::Value value = info[0];
|
|
1993
|
-
|
|
1994
|
-
const TypeInfo *type = ResolveType(info[1]);
|
|
1995
|
-
if (RG_UNLIKELY(!type))
|
|
1996
|
-
return env.Null();
|
|
1997
|
-
if (type->primitive != PrimitiveKind::Pointer &&
|
|
1998
|
-
type->primitive != PrimitiveKind::String &&
|
|
1999
|
-
type->primitive != PrimitiveKind::String16) {
|
|
2000
|
-
ThrowError<Napi::TypeError>(env, "Only pointer or string types can be used for casting");
|
|
2001
|
-
return env.Null();
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
ValueCast *cast = new ValueCast;
|
|
2005
|
-
|
|
2006
|
-
cast->ref.Reset(value, 1);
|
|
2007
|
-
cast->type = type;
|
|
2008
|
-
|
|
2009
|
-
Napi::External<ValueCast> external = Napi::External<ValueCast>::New(env, cast, [](Napi::Env, ValueCast *cast) { delete cast; });
|
|
2010
|
-
SetValueTag(instance, external, &CastMarker);
|
|
2011
|
-
|
|
2012
|
-
return external;
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
static Napi::Value DecodeValue(const Napi::CallbackInfo &info)
|
|
2016
|
-
{
|
|
2017
|
-
Napi::Env env = info.Env();
|
|
2018
|
-
|
|
2019
|
-
bool has_offset = (info.Length() >= 2 && info[1].IsNumber());
|
|
2020
|
-
bool has_len = (info.Length() >= 3u + has_offset && info[2u + has_offset].IsNumber());
|
|
2021
|
-
|
|
2022
|
-
if (RG_UNLIKELY(info.Length() < 2u + has_offset)) {
|
|
2023
|
-
ThrowError<Napi::TypeError>(env, "Expected %1 to 4 arguments, got %2", 2 + has_offset, info.Length());
|
|
2024
|
-
return env.Null();
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
const TypeInfo *type = ResolveType(info[1u + has_offset]);
|
|
2028
|
-
if (RG_UNLIKELY(!type))
|
|
2029
|
-
return env.Null();
|
|
2030
|
-
|
|
2031
|
-
Napi::Value value = info[0];
|
|
2032
|
-
int64_t offset = has_offset ? info[1].As<Napi::Number>().Int64Value() : 0;
|
|
2033
|
-
|
|
2034
|
-
if (has_len) {
|
|
2035
|
-
Size len = info[2u + has_offset].As<Napi::Number>();
|
|
2036
|
-
|
|
2037
|
-
Napi::Value ret = Decode(value, offset, type, &len);
|
|
2038
|
-
return ret;
|
|
2039
|
-
} else {
|
|
2040
|
-
Napi::Value ret = Decode(value, offset, type);
|
|
2041
|
-
return ret;
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
static Napi::Value GetPointerAddress(const Napi::CallbackInfo &info)
|
|
2046
|
-
{
|
|
2047
|
-
Napi::Env env = info.Env();
|
|
2048
|
-
|
|
2049
|
-
if (info.Length() < 1) {
|
|
2050
|
-
ThrowError<Napi::TypeError>(env, "Expected 1 argument, got %1", info.Length());
|
|
2051
|
-
return env.Null();
|
|
2052
|
-
}
|
|
2053
|
-
|
|
2054
|
-
void *ptr = nullptr;
|
|
2055
|
-
if (!GetExternalPointer(env, info[0], &ptr))
|
|
2056
|
-
return env.Null();
|
|
2057
|
-
|
|
2058
|
-
uint64_t ptr64 = (uint64_t)(uintptr_t)ptr;
|
|
2059
|
-
Napi::BigInt bigint = Napi::BigInt::New(env, ptr64);
|
|
2060
|
-
|
|
2061
|
-
return bigint;
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
static Napi::Value CallPointerSync(const Napi::CallbackInfo &info)
|
|
2065
|
-
{
|
|
2066
|
-
Napi::Env env = info.Env();
|
|
2067
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
2068
|
-
|
|
2069
|
-
if (RG_UNLIKELY(info.Length() < 2)) {
|
|
2070
|
-
ThrowError<Napi::TypeError>(env, "Expected 2 or more arguments, got %1", info.Length());
|
|
2071
|
-
return env.Null();
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
void *ptr = nullptr;
|
|
2075
|
-
if (RG_UNLIKELY(!GetExternalPointer(env, info[0], &ptr)))
|
|
2076
|
-
return env.Null();
|
|
2077
|
-
|
|
2078
|
-
const TypeInfo *type = ResolveType(info[1]);
|
|
2079
|
-
if (RG_UNLIKELY(!type))
|
|
2080
|
-
return env.Null();
|
|
2081
|
-
if (RG_UNLIKELY(type->primitive != PrimitiveKind::Prototype)) {
|
|
2082
|
-
ThrowError<Napi::TypeError>(env, "Unexpected %1 value for type, expected function type", GetValueType(instance, info[1]));
|
|
2083
|
-
return env.Null();
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
const FunctionInfo *proto = type->ref.proto;
|
|
2087
|
-
RG_ASSERT(!proto->variadic);
|
|
2088
|
-
|
|
2089
|
-
return TranslateNormalCall(proto, ptr, info);
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
extern "C" void RelayCallback(Size idx, uint8_t *own_sp, uint8_t *caller_sp, BackRegisters *out_reg)
|
|
2098
|
+
static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
|
|
2093
2099
|
{
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
} else {
|
|
2097
|
-
// This happens if the callback pointer is called from a different thread
|
|
2098
|
-
// than the one that runs the FFI call (sync or async).
|
|
2099
|
-
|
|
2100
|
-
TrampolineInfo *trampoline = &shared.trampolines[idx];
|
|
2101
|
-
|
|
2102
|
-
Napi::Env env = trampoline->func.Env();
|
|
2103
|
-
InstanceData *instance = env.GetInstanceData<InstanceData>();
|
|
2104
|
-
|
|
2105
|
-
InstanceMemory *mem = AllocateMemory(instance, instance->config.async_stack_size, instance->config.async_heap_size);
|
|
2106
|
-
if (RG_UNLIKELY(!mem)) {
|
|
2107
|
-
ThrowError<Napi::Error>(env, "Too many asynchronous calls are running");
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
// Avoid triggering the "use callback beyond FFI" check
|
|
2112
|
-
RG_DEFER_C(generation = trampoline->generation) { trampoline->generation = generation; };
|
|
2113
|
-
trampoline->generation = -1;
|
|
2100
|
+
InstanceData *instance = CreateInstance(env);
|
|
2101
|
+
RG_CRITICAL(instance, "Failed to initialize Koffi");
|
|
2114
2102
|
|
|
2115
|
-
|
|
2116
|
-
CallData call(env, instance, mem);
|
|
2117
|
-
call.RelaySafe(idx, own_sp, caller_sp, true, out_reg);
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2103
|
+
env.SetInstanceData(instance);
|
|
2120
2104
|
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
{
|
|
2124
|
-
func("config", Napi::Function::New(env, GetSetConfig));
|
|
2125
|
-
func("stats", Napi::Function::New(env, GetStats));
|
|
2105
|
+
exports.Set("config", Napi::Function::New(env, GetSetConfig));
|
|
2106
|
+
exports.Set("stats", Napi::Function::New(env, GetStats));
|
|
2126
2107
|
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2108
|
+
exports.Set("struct", Napi::Function::New(env, CreatePaddedStructType));
|
|
2109
|
+
exports.Set("pack", Napi::Function::New(env, CreatePackedStructType));
|
|
2110
|
+
exports.Set("union", Napi::Function::New(env, CreateUnionType));
|
|
2111
|
+
exports.Set("Union", Napi::Function::New(env, InstantiateUnion));
|
|
2112
|
+
exports.Set("opaque", Napi::Function::New(env, CreateOpaqueType));
|
|
2113
|
+
exports.Set("pointer", Napi::Function::New(env, CreatePointerType));
|
|
2114
|
+
exports.Set("array", Napi::Function::New(env, CreateArrayType));
|
|
2115
|
+
exports.Set("proto", Napi::Function::New(env, CreateFunctionType));
|
|
2116
|
+
exports.Set("alias", Napi::Function::New(env, CreateTypeAlias));
|
|
2136
2117
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2118
|
+
exports.Set("sizeof", Napi::Function::New(env, GetTypeSize));
|
|
2119
|
+
exports.Set("alignof", Napi::Function::New(env, GetTypeAlign));
|
|
2120
|
+
exports.Set("offsetof", Napi::Function::New(env, GetMemberOffset));
|
|
2121
|
+
exports.Set("resolve", Napi::Function::New(env, GetResolvedType));
|
|
2122
|
+
exports.Set("introspect", Napi::Function::New(env, GetTypeDefinition));
|
|
2142
2123
|
|
|
2143
|
-
|
|
2124
|
+
exports.Set("load", Napi::Function::New(env, LoadSharedLibrary));
|
|
2144
2125
|
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2126
|
+
exports.Set("in", Napi::Function::New(env, MarkIn));
|
|
2127
|
+
exports.Set("out", Napi::Function::New(env, MarkOut));
|
|
2128
|
+
exports.Set("inout", Napi::Function::New(env, MarkInOut));
|
|
2148
2129
|
|
|
2149
|
-
|
|
2150
|
-
|
|
2130
|
+
exports.Set("disposable", Napi::Function::New(env, CreateDisposableType));
|
|
2131
|
+
exports.Set("free", Napi::Function::New(env, CallFree));
|
|
2151
2132
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2133
|
+
exports.Set("register", Napi::Function::New(env, RegisterCallback));
|
|
2134
|
+
exports.Set("unregister", Napi::Function::New(env, UnregisterCallback));
|
|
2154
2135
|
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2136
|
+
exports.Set("as", Napi::Function::New(env, CastValue));
|
|
2137
|
+
exports.Set("decode", Napi::Function::New(env, DecodeValue));
|
|
2138
|
+
exports.Set("address", Napi::Function::New(env, GetPointerAddress));
|
|
2139
|
+
exports.Set("call", Napi::Function::New(env, CallPointerSync));
|
|
2159
2140
|
|
|
2160
|
-
|
|
2141
|
+
exports.Set("errno", Napi::Function::New(env, GetOrSetErrNo));
|
|
2161
2142
|
|
|
2162
2143
|
Napi::Object os = Napi::Object::New(env);
|
|
2163
|
-
|
|
2144
|
+
exports.Set("os", os);
|
|
2164
2145
|
|
|
2165
2146
|
// Init constants mapping
|
|
2166
2147
|
{
|
|
@@ -2174,85 +2155,47 @@ static void SetExports(Napi::Env env, Func func)
|
|
|
2174
2155
|
}
|
|
2175
2156
|
|
|
2176
2157
|
#if defined(_WIN32)
|
|
2177
|
-
|
|
2158
|
+
exports.Set("extension", Napi::String::New(env, ".dll"));
|
|
2178
2159
|
#elif defined(__APPLE__)
|
|
2179
|
-
|
|
2160
|
+
exports.Set("extension", Napi::String::New(env, ".dylib"));
|
|
2180
2161
|
#else
|
|
2181
|
-
|
|
2162
|
+
exports.Set("extension", Napi::String::New(env, ".so"));
|
|
2182
2163
|
#endif
|
|
2183
2164
|
|
|
2184
2165
|
Napi::Object types = InitBaseTypes(env);
|
|
2185
|
-
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
#if NODE_WANT_INTERNALS
|
|
2166
|
+
exports.Set("types", types);
|
|
2191
2167
|
|
|
2192
|
-
|
|
2193
|
-
const char *name, Napi::Value value)
|
|
2194
|
-
{
|
|
2195
|
-
v8::Isolate *isolate = env->isolate();
|
|
2196
|
-
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
2197
|
-
|
|
2198
|
-
v8::NewStringType str_type = v8::NewStringType::kInternalized;
|
|
2199
|
-
v8::Local<v8::String> str = v8::String::NewFromUtf8(isolate, name, str_type).ToLocalChecked();
|
|
2168
|
+
exports.Set("internal", Napi::Boolean::New(env, false));
|
|
2200
2169
|
|
|
2201
|
-
|
|
2170
|
+
return exports;
|
|
2202
2171
|
}
|
|
2203
2172
|
|
|
2204
|
-
|
|
2205
|
-
v8::Local<v8::Context> context, void *)
|
|
2173
|
+
InstanceData::~InstanceData()
|
|
2206
2174
|
{
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
// Not very clean but I don't know enough about Node and V8 to do better...
|
|
2212
|
-
// ... and it seems to work okay.
|
|
2213
|
-
napi_env env_napi = new napi_env__(context);
|
|
2214
|
-
Napi::Env env_cxx(env_napi);
|
|
2215
|
-
env->AtExit([](void *udata) {
|
|
2216
|
-
napi_env env_napi = (napi_env)udata;
|
|
2217
|
-
delete env_napi;
|
|
2218
|
-
}, env_napi);
|
|
2175
|
+
for (InstanceMemory *mem: memories) {
|
|
2176
|
+
delete mem;
|
|
2177
|
+
}
|
|
2219
2178
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2179
|
+
// Clean-up leftover trampoline references
|
|
2180
|
+
{
|
|
2181
|
+
std::lock_guard<std::mutex> lock(shared.mutex);
|
|
2222
2182
|
|
|
2223
|
-
|
|
2183
|
+
for (int16_t idx = 0; idx < MaxTrampolines; idx++) {
|
|
2184
|
+
TrampolineInfo *trampoline = &shared.trampolines[idx];
|
|
2224
2185
|
|
|
2225
|
-
|
|
2226
|
-
|
|
2186
|
+
if (trampoline->instance == this) {
|
|
2187
|
+
trampoline->instance = nullptr;
|
|
2188
|
+
trampoline->func.Reset();
|
|
2189
|
+
trampoline->recv.Reset();
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2227
2193
|
|
|
2228
|
-
|
|
2229
|
-
|
|
2194
|
+
if (broker) {
|
|
2195
|
+
napi_release_threadsafe_function(broker, napi_tsfn_abort);
|
|
2196
|
+
}
|
|
2230
2197
|
}
|
|
2231
2198
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
static Napi::Object InitModule(Napi::Env env, Napi::Object exports)
|
|
2235
|
-
{
|
|
2236
|
-
using namespace RG;
|
|
2237
|
-
|
|
2238
|
-
InstanceData *instance = CreateInstance(env);
|
|
2239
|
-
RG_CRITICAL(instance, "Failed to initialize Koffi");
|
|
2240
|
-
|
|
2241
|
-
env.SetInstanceData(instance);
|
|
2242
|
-
|
|
2243
|
-
instance->debug = GetDebugFlag("DUMP_CALLS");
|
|
2244
|
-
FillRandomSafe(&instance->tag_lower, RG_SIZE(instance->tag_lower));
|
|
2245
|
-
|
|
2246
|
-
SetExports(env, [&](const char *name, Napi::Value value) { exports.Set(name, value); });
|
|
2247
|
-
exports.Set("internal", Napi::Boolean::New(env, false));
|
|
2199
|
+
NODE_API_MODULE(koffi, InitModule);
|
|
2248
2200
|
|
|
2249
|
-
return exports;
|
|
2250
2201
|
}
|
|
2251
|
-
|
|
2252
|
-
#endif
|
|
2253
|
-
|
|
2254
|
-
#if NODE_WANT_INTERNALS
|
|
2255
|
-
NODE_MODULE_CONTEXT_AWARE_INTERNAL(koffi, InitInternal);
|
|
2256
|
-
#else
|
|
2257
|
-
NODE_API_MODULE(koffi, InitModule);
|
|
2258
|
-
#endif
|
package/src/koffi/src/ffi.hh
CHANGED
|
@@ -198,7 +198,7 @@ struct ParameterInfo {
|
|
|
198
198
|
int8_t vec_bytes; // ARM64
|
|
199
199
|
#elif defined(__i386__) || defined(_M_IX86)
|
|
200
200
|
bool trivial; // Only matters for return value
|
|
201
|
-
|
|
201
|
+
int8_t fast;
|
|
202
202
|
#elif __riscv_xlen == 64
|
|
203
203
|
bool use_memory;
|
|
204
204
|
int8_t gpr_count;
|
|
@@ -273,6 +273,8 @@ struct InstanceData {
|
|
|
273
273
|
const TypeInfo *char_type;
|
|
274
274
|
const TypeInfo *char16_type;
|
|
275
275
|
|
|
276
|
+
Napi::Symbol active_symbol;
|
|
277
|
+
|
|
276
278
|
std::mutex memories_mutex;
|
|
277
279
|
LocalArray<InstanceMemory *, 9> memories;
|
|
278
280
|
int temporaries = 0;
|