koffi 0.9.0

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.
Files changed (131) hide show
  1. package/CMakeLists.txt +94 -0
  2. package/README.md +153 -0
  3. package/build/ALL_BUILD.vcxproj +190 -0
  4. package/build/ALL_BUILD.vcxproj.filters +8 -0
  5. package/build/CMakeCache.txt +429 -0
  6. package/build/CMakeFiles/3.23.0-rc1/CMakeASMCompiler.cmake +20 -0
  7. package/build/CMakeFiles/3.23.0-rc1/CMakeASM_MASMCompiler.cmake +20 -0
  8. package/build/CMakeFiles/3.23.0-rc1/CMakeCCompiler.cmake +72 -0
  9. package/build/CMakeFiles/3.23.0-rc1/CMakeCXXCompiler.cmake +83 -0
  10. package/build/CMakeFiles/3.23.0-rc1/CMakeDetermineCompilerABI_C.bin +0 -0
  11. package/build/CMakeFiles/3.23.0-rc1/CMakeDetermineCompilerABI_CXX.bin +0 -0
  12. package/build/CMakeFiles/3.23.0-rc1/CMakeRCCompiler.cmake +6 -0
  13. package/build/CMakeFiles/3.23.0-rc1/CMakeSystem.cmake +15 -0
  14. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CMakeCCompilerId.c +828 -0
  15. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CompilerIdC.exe +0 -0
  16. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/CompilerIdC.vcxproj +71 -0
  17. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CMakeCCompilerId.obj +0 -0
  18. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.exe.recipe +11 -0
  19. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.command.1.tlog +0 -0
  20. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.read.1.tlog +0 -0
  21. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CL.write.1.tlog +0 -0
  22. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/CompilerIdC.lastbuildstate +2 -0
  23. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.command.1.tlog +0 -0
  24. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.read.1.tlog +0 -0
  25. package/build/CMakeFiles/3.23.0-rc1/CompilerIdC/Debug/CompilerIdC.tlog/link.write.1.tlog +0 -0
  26. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CMakeCXXCompilerId.cpp +816 -0
  27. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CompilerIdCXX.exe +0 -0
  28. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/CompilerIdCXX.vcxproj +71 -0
  29. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CMakeCXXCompilerId.obj +0 -0
  30. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.exe.recipe +11 -0
  31. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.command.1.tlog +0 -0
  32. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.read.1.tlog +0 -0
  33. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CL.write.1.tlog +0 -0
  34. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/CompilerIdCXX.lastbuildstate +2 -0
  35. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.command.1.tlog +0 -0
  36. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.read.1.tlog +0 -0
  37. package/build/CMakeFiles/3.23.0-rc1/CompilerIdCXX/Debug/CompilerIdCXX.tlog/link.write.1.tlog +0 -0
  38. package/build/CMakeFiles/3.23.0-rc1/VCTargetsPath.txt +1 -0
  39. package/build/CMakeFiles/3.23.0-rc1/VCTargetsPath.vcxproj +31 -0
  40. package/build/CMakeFiles/3.23.0-rc1/x64/Debug/VCTargetsPath.recipe +11 -0
  41. package/build/CMakeFiles/3.23.0-rc1/x64/Debug/VCTargetsPath.tlog/VCTargetsPath.lastbuildstate +2 -0
  42. package/build/CMakeFiles/41bcd16856091d4a38fd1f71fbe2f202/generate.stamp.rule +1 -0
  43. package/build/CMakeFiles/CMakeError.log +108 -0
  44. package/build/CMakeFiles/CMakeOutput.log +413 -0
  45. package/build/CMakeFiles/TargetDirectories.txt +4 -0
  46. package/build/CMakeFiles/cmake.check_cache +1 -0
  47. package/build/CMakeFiles/generate.stamp +1 -0
  48. package/build/CMakeFiles/generate.stamp.depend +28 -0
  49. package/build/CMakeFiles/generate.stamp.list +1 -0
  50. package/build/Raylib.dir/Release/Raylib.dll.recipe +14 -0
  51. package/build/Raylib.dir/Release/Raylib.tlog/CL.command.1.tlog +0 -0
  52. package/build/Raylib.dir/Release/Raylib.tlog/CL.read.1.tlog +0 -0
  53. package/build/Raylib.dir/Release/Raylib.tlog/CL.write.1.tlog +0 -0
  54. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.command.1.tlog +10 -0
  55. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.read.1.tlog +27 -0
  56. package/build/Raylib.dir/Release/Raylib.tlog/CustomBuild.write.1.tlog +2 -0
  57. package/build/Raylib.dir/Release/Raylib.tlog/Raylib.lastbuildstate +2 -0
  58. package/build/Raylib.dir/Release/Raylib.tlog/Raylib.write.1u.tlog +0 -0
  59. package/build/Raylib.dir/Release/Raylib.tlog/link.command.1.tlog +0 -0
  60. package/build/Raylib.dir/Release/Raylib.tlog/link.read.1.tlog +0 -0
  61. package/build/Raylib.dir/Release/Raylib.tlog/link.write.1.tlog +0 -0
  62. package/build/Raylib.dir/Release/raudio.obj +0 -0
  63. package/build/Raylib.dir/Release/rcore.obj +0 -0
  64. package/build/Raylib.dir/Release/rglfw.obj +0 -0
  65. package/build/Raylib.dir/Release/rmodels.obj +0 -0
  66. package/build/Raylib.dir/Release/rshapes.obj +0 -0
  67. package/build/Raylib.dir/Release/rtext.obj +0 -0
  68. package/build/Raylib.dir/Release/rtextures.obj +0 -0
  69. package/build/Raylib.dir/Release/utils.obj +0 -0
  70. package/build/Raylib.vcxproj +358 -0
  71. package/build/Raylib.vcxproj.filters +37 -0
  72. package/build/Release/Raylib.dll +0 -0
  73. package/build/Release/Raylib.exp +0 -0
  74. package/build/Release/Raylib.lib +0 -0
  75. package/build/Release/koffi.exp +0 -0
  76. package/build/Release/koffi.lib +0 -0
  77. package/build/Release/koffi.node +0 -0
  78. package/build/ZERO_CHECK.vcxproj +176 -0
  79. package/build/ZERO_CHECK.vcxproj.filters +13 -0
  80. package/build/cmake_install.cmake +44 -0
  81. package/build/koffi.dir/Release/call_arm64.obj +0 -0
  82. package/build/koffi.dir/Release/call_x64_sysv.obj +0 -0
  83. package/build/koffi.dir/Release/call_x64_win.obj +0 -0
  84. package/build/koffi.dir/Release/call_x64_win_fwd.obj +0 -0
  85. package/build/koffi.dir/Release/call_x86.obj +0 -0
  86. package/build/koffi.dir/Release/ffi.obj +0 -0
  87. package/build/koffi.dir/Release/koffi.node.recipe +14 -0
  88. package/build/koffi.dir/Release/koffi.tlog/CL.command.1.tlog +0 -0
  89. package/build/koffi.dir/Release/koffi.tlog/CL.read.1.tlog +0 -0
  90. package/build/koffi.dir/Release/koffi.tlog/CL.write.1.tlog +0 -0
  91. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.command.1.tlog +10 -0
  92. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.read.1.tlog +27 -0
  93. package/build/koffi.dir/Release/koffi.tlog/CustomBuild.write.1.tlog +2 -0
  94. package/build/koffi.dir/Release/koffi.tlog/Masm.read.1u.tlog +0 -0
  95. package/build/koffi.dir/Release/koffi.tlog/Masm.write.1u.tlog +0 -0
  96. package/build/koffi.dir/Release/koffi.tlog/koffi.lastbuildstate +2 -0
  97. package/build/koffi.dir/Release/koffi.tlog/koffi.write.1u.tlog +0 -0
  98. package/build/koffi.dir/Release/koffi.tlog/link.command.1.tlog +0 -0
  99. package/build/koffi.dir/Release/koffi.tlog/link.read.1.tlog +0 -0
  100. package/build/koffi.dir/Release/koffi.tlog/link.write.1.tlog +0 -0
  101. package/build/koffi.dir/Release/libcc.obj +0 -0
  102. package/build/koffi.dir/Release/util.obj +0 -0
  103. package/build/koffi.dir/Release/win_delay_load_hook.obj +0 -0
  104. package/build/koffi.sln +67 -0
  105. package/build/koffi.vcxproj +363 -0
  106. package/build/koffi.vcxproj.filters +40 -0
  107. package/build/x64/Release/ALL_BUILD/ALL_BUILD.recipe +20 -0
  108. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/ALL_BUILD.lastbuildstate +2 -0
  109. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.command.1.tlog +10 -0
  110. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.read.1.tlog +27 -0
  111. package/build/x64/Release/ALL_BUILD/ALL_BUILD.tlog/CustomBuild.write.1.tlog +2 -0
  112. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.recipe +11 -0
  113. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.command.1.tlog +10 -0
  114. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.read.1.tlog +28 -0
  115. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/CustomBuild.write.1.tlog +2 -0
  116. package/build/x64/Release/ZERO_CHECK/ZERO_CHECK.tlog/ZERO_CHECK.lastbuildstate +2 -0
  117. package/package.json +14 -0
  118. package/src/call.hh +27 -0
  119. package/src/call_arm64.cc +482 -0
  120. package/src/call_arm64_fwd.S +115 -0
  121. package/src/call_x64_sysv.cc +477 -0
  122. package/src/call_x64_sysv_fwd.S +131 -0
  123. package/src/call_x64_win.cc +243 -0
  124. package/src/call_x64_win_fwd.asm +105 -0
  125. package/src/call_x86.cc +259 -0
  126. package/src/call_x86_fwd.S +48 -0
  127. package/src/call_x86_fwd.asm +50 -0
  128. package/src/ffi.cc +504 -0
  129. package/src/ffi.hh +135 -0
  130. package/src/util.cc +296 -0
  131. package/src/util.hh +80 -0
@@ -0,0 +1,243 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #ifdef _WIN64
15
+
16
+ #include "vendor/libcc/libcc.hh"
17
+ #include "ffi.hh"
18
+ #include "call.hh"
19
+ #include "util.hh"
20
+
21
+ #include <napi.h>
22
+
23
+ namespace RG {
24
+
25
+ extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp);
26
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp);
27
+ extern "C" double ForwardCallD(const void *func, uint8_t *sp);
28
+ extern "C" uint64_t ForwardCallXG(const void *func, uint8_t *sp);
29
+ extern "C" float ForwardCallXF(const void *func, uint8_t *sp);
30
+ extern "C" double ForwardCallXD(const void *func, uint8_t *sp);
31
+
32
+ static inline bool IsRegular(Size size)
33
+ {
34
+ bool regular = (size <= 8 && !(size & (size - 1)));
35
+ return regular;
36
+ }
37
+
38
+ bool AnalyseFunction(FunctionInfo *func)
39
+ {
40
+ func->ret.regular = IsRegular(func->ret.type->size);
41
+
42
+ for (ParameterInfo &param: func->parameters) {
43
+ param.regular = IsRegular(param.type->size);
44
+
45
+ func->forward_fp |= (param.type->primitive == PrimitiveKind::Float32 ||
46
+ param.type->primitive == PrimitiveKind::Float64);
47
+ }
48
+
49
+ return true;
50
+ }
51
+
52
+ Napi::Value TranslateCall(const Napi::CallbackInfo &info)
53
+ {
54
+ Napi::Env env = info.Env();
55
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
56
+
57
+ FunctionInfo *func = (FunctionInfo *)info.Data();
58
+ LibraryData *lib = func->lib.get();
59
+
60
+ RG_DEFER { lib->tmp_alloc.ReleaseAll(); };
61
+
62
+ // Sanity checks
63
+ if (info.Length() < (uint32_t)func->parameters.len) {
64
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
65
+ return env.Null();
66
+ }
67
+
68
+ // Stack pointer and register
69
+ uint8_t *top_ptr = lib->stack.end();
70
+ uint8_t *scratch_ptr = top_ptr - func->scratch_size;
71
+ uint8_t *return_ptr = nullptr;
72
+ uint8_t *args_ptr = nullptr;
73
+
74
+ // Reserve space for return value if needed
75
+ if (func->ret.regular) {
76
+ args_ptr = scratch_ptr - AlignLen(8 * std::max((Size)4, func->parameters.len), 16);
77
+ } else {
78
+ return_ptr = scratch_ptr - AlignLen(func->ret.type->size, 16);
79
+
80
+ args_ptr = return_ptr - AlignLen(8 * std::max((Size)4, func->parameters.len + 1), 16);
81
+ *(uint64_t *)args_ptr = (uint64_t)return_ptr;
82
+ }
83
+
84
+ RG_ASSERT(AlignUp(lib->stack.ptr, 16) == lib->stack.ptr);
85
+ RG_ASSERT(AlignUp(lib->stack.end(), 16) == lib->stack.end());
86
+ RG_ASSERT(AlignUp(args_ptr, 16) == args_ptr);
87
+
88
+ // Push arguments
89
+ for (Size i = 0, j = return_ptr ? 8 : 0; i < func->parameters.len; i++, j += 8) {
90
+ const ParameterInfo &param = func->parameters[i];
91
+ Napi::Value value = info[i];
92
+
93
+ switch (param.type->primitive) {
94
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
95
+
96
+ case PrimitiveKind::Bool: {
97
+ if (RG_UNLIKELY(!value.IsBoolean())) {
98
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected boolean", GetValueType(instance, value), i + 1);
99
+ return env.Null();
100
+ }
101
+
102
+ bool b = value.As<Napi::Boolean>();
103
+ *(bool *)(args_ptr + j) = b;
104
+ } break;
105
+ case PrimitiveKind::Int8:
106
+ case PrimitiveKind::UInt8:
107
+ case PrimitiveKind::Int16:
108
+ case PrimitiveKind::UInt16:
109
+ case PrimitiveKind::Int32:
110
+ case PrimitiveKind::UInt32:
111
+ case PrimitiveKind::Int64:
112
+ case PrimitiveKind::UInt64: {
113
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
114
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
115
+ return env.Null();
116
+ }
117
+
118
+ int64_t v = CopyNodeNumber<int64_t>(value);
119
+ *(uint64_t *)(args_ptr + j) = (uint64_t)v;
120
+ } break;
121
+ case PrimitiveKind::Float32: {
122
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
123
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
124
+ return env.Null();
125
+ }
126
+
127
+ float f = CopyNodeNumber<float>(value);
128
+ *(float *)(args_ptr + j) = f;
129
+ } break;
130
+ case PrimitiveKind::Float64: {
131
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
132
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
133
+ return env.Null();
134
+ }
135
+
136
+ double d = CopyNodeNumber<double>(value);
137
+ *(double *)(args_ptr + j) = d;
138
+ } break;
139
+ case PrimitiveKind::String: {
140
+ if (RG_UNLIKELY(!value.IsString())) {
141
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
142
+ return env.Null();
143
+ }
144
+
145
+ const char *str = CopyNodeString(value, &lib->tmp_alloc);
146
+ *(const char **)(args_ptr + j) = str;
147
+ } break;
148
+ case PrimitiveKind::Pointer: {
149
+ if (RG_UNLIKELY(!CheckValueTag(instance, value, param.type))) {
150
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
151
+ return env.Null();
152
+ }
153
+
154
+ void *ptr = value.As<Napi::External<void>>();
155
+ *(void **)(args_ptr + j) = ptr;
156
+ } break;
157
+
158
+ case PrimitiveKind::Record: {
159
+ if (RG_UNLIKELY(!value.IsObject())) {
160
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected object", GetValueType(instance, value), i + 1);
161
+ return env.Null();
162
+ }
163
+
164
+ uint8_t *ptr;
165
+ if (param.regular) {
166
+ ptr = (uint8_t *)(args_ptr + j);
167
+ } else {
168
+ ptr = scratch_ptr;
169
+ *(uint8_t **)(args_ptr + j) = ptr;
170
+
171
+ scratch_ptr = AlignUp(scratch_ptr + param.type->size, 16);
172
+ }
173
+
174
+ Napi::Object obj = value.As<Napi::Object>();
175
+ if (!PushObject(obj, param.type, &lib->tmp_alloc, ptr))
176
+ return env.Null();
177
+ } break;
178
+ }
179
+ }
180
+
181
+ // DumpStack(func, MakeSpan(args_ptr, top_ptr - args_ptr));
182
+
183
+ #define PERFORM_CALL(Suffix) \
184
+ (func->forward_fp ? ForwardCallX ## Suffix(func->func, args_ptr) \
185
+ : ForwardCall ## Suffix(func->func, args_ptr))
186
+
187
+ // Execute and convert return value
188
+ switch (func->ret.type->primitive) {
189
+ case PrimitiveKind::Float32: {
190
+ float f = PERFORM_CALL(F);
191
+
192
+ return Napi::Number::New(env, (double)f);
193
+ } break;
194
+
195
+ case PrimitiveKind::Float64: {
196
+ double d = PERFORM_CALL(D);
197
+
198
+ return Napi::Number::New(env, d);
199
+ } break;
200
+
201
+ default: {
202
+ uint64_t rax = PERFORM_CALL(G);
203
+
204
+ switch (func->ret.type->primitive) {
205
+ case PrimitiveKind::Void: return env.Null();
206
+ case PrimitiveKind::Bool: return Napi::Boolean::New(env, rax);
207
+ case PrimitiveKind::Int8: return Napi::Number::New(env, (double)rax);
208
+ case PrimitiveKind::UInt8: return Napi::Number::New(env, (double)rax);
209
+ case PrimitiveKind::Int16: return Napi::Number::New(env, (double)rax);
210
+ case PrimitiveKind::UInt16: return Napi::Number::New(env, (double)rax);
211
+ case PrimitiveKind::Int32: return Napi::Number::New(env, (double)rax);
212
+ case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)rax);
213
+ case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)rax);
214
+ case PrimitiveKind::UInt64: return Napi::BigInt::New(env, rax);
215
+ case PrimitiveKind::Float32: { RG_UNREACHABLE(); } break;
216
+ case PrimitiveKind::Float64: { RG_UNREACHABLE(); } break;
217
+ case PrimitiveKind::String: return Napi::String::New(env, (const char *)rax);
218
+ case PrimitiveKind::Pointer: {
219
+ void *ptr = (void *)rax;
220
+
221
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
222
+ SetValueTag(instance, external, func->ret.type);
223
+
224
+ return external;
225
+ } break;
226
+
227
+ case PrimitiveKind::Record: {
228
+ const uint8_t *ptr = return_ptr ? return_ptr : (const uint8_t *)&rax;
229
+ Napi::Object obj = PopObject(env, ptr, func->ret.type);
230
+ return obj;
231
+ } break;
232
+ }
233
+ } break;
234
+ }
235
+
236
+ #undef PERFORM_CALL
237
+
238
+ RG_UNREACHABLE();
239
+ }
240
+
241
+ }
242
+
243
+ #endif
@@ -0,0 +1,105 @@
1
+ ; This program is free software: you can redistribute it and/or modify
2
+ ; it under the terms of the GNU Affero General Public License as published by
3
+ ; the Free Software Foundation, either version 3 of the License, or
4
+ ; (at your option) any later version.
5
+ ;
6
+ ; This program is distributed in the hope that it will be useful,
7
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ ; GNU Affero General Public License for more details.
10
+ ;
11
+ ; You should have received a copy of the GNU Affero General Public License
12
+ ; along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ ; These three are the same, but they differ (in the C side) by their return type.
15
+ ; Unlike the three next functions, these ones don't forward XMM argument registers.
16
+ public ForwardCallG
17
+ public ForwardCallF
18
+ public ForwardCallD
19
+
20
+ ; The X variants are slightly slower, and are used when XMM arguments must be forwarded.
21
+ public ForwardCallXG
22
+ public ForwardCallXF
23
+ public ForwardCallXD
24
+
25
+ .code
26
+
27
+ ; Copy function pointer to RAX, in order to save it through argument forwarding.
28
+ ; Save RSP in RBX (non-volatile), and use carefully assembled stack provided by caller.
29
+ prologue macro
30
+ endbr64
31
+ mov rax, rcx
32
+ push rbx
33
+ .pushreg rbx
34
+ mov rbx, rsp
35
+ .setframe rbx, 0
36
+ .endprolog
37
+ mov rsp, rdx
38
+ endm
39
+
40
+ ; Call native function.
41
+ ; Once done, restore normal stack pointer and return.
42
+ ; The return value is passed untouched through RAX or XMM0.
43
+ epilogue macro
44
+ call rax
45
+ mov rsp, rbx
46
+ pop rbx
47
+ ret
48
+ endm
49
+
50
+ ; Prepare integer argument registers from array passed by caller.
51
+ forward_int macro
52
+ mov r9, qword ptr [rdx+24]
53
+ mov r8, qword ptr [rdx+16]
54
+ mov rcx, qword ptr [rdx+0]
55
+ mov rdx, qword ptr [rdx+8]
56
+ endm
57
+
58
+ ; Prepare XMM argument registers from array passed by caller.
59
+ forward_xmm macro
60
+ movsd xmm3, qword ptr [rdx+24]
61
+ movsd xmm2, qword ptr [rdx+16]
62
+ movsd xmm1, qword ptr [rdx+8]
63
+ movsd xmm0, qword ptr [rdx+0]
64
+ endm
65
+
66
+ ForwardCallG proc frame
67
+ prologue
68
+ forward_int
69
+ epilogue
70
+ ForwardCallG endp
71
+
72
+ ForwardCallF proc frame
73
+ prologue
74
+ forward_int
75
+ epilogue
76
+ ForwardCallF endp
77
+
78
+ ForwardCallD proc frame
79
+ prologue
80
+ forward_int
81
+ epilogue
82
+ ForwardCallD endp
83
+
84
+ ForwardCallXG proc frame
85
+ prologue
86
+ forward_xmm
87
+ forward_int
88
+ epilogue
89
+ ForwardCallXG endp
90
+
91
+ ForwardCallXF proc frame
92
+ prologue
93
+ forward_xmm
94
+ forward_int
95
+ epilogue
96
+ ForwardCallXF endp
97
+
98
+ ForwardCallXD proc frame
99
+ prologue
100
+ forward_xmm
101
+ forward_int
102
+ epilogue
103
+ ForwardCallXD endp
104
+
105
+ end
@@ -0,0 +1,259 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ #if defined(__i386__) || defined(_M_IX86)
15
+
16
+ #include "vendor/libcc/libcc.hh"
17
+ #include "ffi.hh"
18
+ #include "call.hh"
19
+ #include "util.hh"
20
+
21
+ #include <napi.h>
22
+
23
+ namespace RG {
24
+
25
+ extern "C" uint64_t ForwardCallG(const void *func, uint8_t *sp);
26
+ extern "C" float ForwardCallF(const void *func, uint8_t *sp);
27
+ extern "C" double ForwardCallD(const void *func, uint8_t *sp);
28
+
29
+ bool AnalyseFunction(FunctionInfo *func)
30
+ {
31
+ #ifdef _WIN32
32
+ if (func->convention == CallConvention::Stdcall) {
33
+ Size total = 0;
34
+ for (const ParameterInfo &param: func->parameters) {
35
+ total += param.type->size;
36
+ }
37
+
38
+ func->decorated_name = Fmt(&func->lib->str_alloc, "_%1@%2", func->name, total).ptr;
39
+ }
40
+ #endif
41
+
42
+ if (IsIntegral(func->ret.type->primitive)) {
43
+ func->ret.trivial = true;
44
+ } else if (func->ret.type->members.len == 1 && IsIntegral(func->ret.type->members[0].type->primitive)) {
45
+ func->ret.trivial = true;
46
+ #ifdef _WIN32
47
+ } else if (func->ret.type->members.len == 2 && IsIntegral(func->ret.type->members[0].type->primitive)
48
+ && IsIntegral(func->ret.type->members[1].type->primitive)) {
49
+ func->ret.trivial = true;
50
+ #endif
51
+ }
52
+
53
+ return true;
54
+ }
55
+
56
+ Napi::Value TranslateCall(const Napi::CallbackInfo &info)
57
+ {
58
+ Napi::Env env = info.Env();
59
+ InstanceData *instance = env.GetInstanceData<InstanceData>();
60
+
61
+ FunctionInfo *func = (FunctionInfo *)info.Data();
62
+ LibraryData *lib = func->lib.get();
63
+
64
+ RG_DEFER { lib->tmp_alloc.ReleaseAll(); };
65
+
66
+ // Sanity checks
67
+ if (info.Length() < (uint32_t)func->parameters.len) {
68
+ ThrowError<Napi::TypeError>(env, "Expected %1 arguments, got %2", func->parameters.len, info.Length());
69
+ return env.Null();
70
+ }
71
+
72
+ // Stack pointer and register
73
+ uint8_t *top_ptr = lib->stack.end();
74
+ uint8_t *return_ptr = nullptr;
75
+ uint8_t *args_ptr = nullptr;
76
+ uint8_t *sp_ptr = nullptr;
77
+
78
+ // Reserve space for return value if needed
79
+ if (func->ret.trivial) {
80
+ args_ptr = top_ptr - func->scratch_size;
81
+ sp_ptr = args_ptr;
82
+ } else {
83
+ return_ptr = top_ptr - AlignLen(func->ret.type->size, 16);
84
+ args_ptr = return_ptr - func->scratch_size;
85
+ sp_ptr = args_ptr;
86
+
87
+ *(uint32_t *)args_ptr = (uint32_t)return_ptr;
88
+ args_ptr += 4;
89
+ }
90
+
91
+ RG_ASSERT(AlignUp(lib->stack.ptr, 16) == lib->stack.ptr);
92
+ RG_ASSERT(AlignUp(lib->stack.end(), 16) == lib->stack.end());
93
+ RG_ASSERT(AlignUp(args_ptr, 16) == args_ptr);
94
+
95
+ // Push arguments
96
+ for (Size i = 0; i < func->parameters.len; i++) {
97
+ const ParameterInfo &param = func->parameters[i];
98
+ Napi::Value value = info[i];
99
+
100
+ switch (param.type->primitive) {
101
+ case PrimitiveKind::Void: { RG_UNREACHABLE(); } break;
102
+
103
+ case PrimitiveKind::Bool: {
104
+ if (RG_UNLIKELY(!value.IsBoolean())) {
105
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected boolean", GetValueType(instance, value), i + 1);
106
+ return env.Null();
107
+ }
108
+
109
+ bool b = value.As<Napi::Boolean>();
110
+ *(bool *)args_ptr = b;
111
+ args_ptr += 4;
112
+ } break;
113
+ case PrimitiveKind::Int8:
114
+ case PrimitiveKind::UInt8:
115
+ case PrimitiveKind::Int16:
116
+ case PrimitiveKind::UInt16:
117
+ case PrimitiveKind::Int32:
118
+ case PrimitiveKind::UInt32: {
119
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
120
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
121
+ return env.Null();
122
+ }
123
+
124
+ int32_t v = CopyNodeNumber<int32_t>(value);
125
+ *(uint32_t *)args_ptr = (uint32_t)v;
126
+ args_ptr += 4;
127
+ } break;
128
+ case PrimitiveKind::Int64:
129
+ case PrimitiveKind::UInt64: {
130
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
131
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
132
+ return env.Null();
133
+ }
134
+
135
+ int64_t v = CopyNodeNumber<int64_t>(value);
136
+ *(uint64_t *)args_ptr = (uint64_t)v;
137
+ args_ptr += 8;
138
+ } break;
139
+ case PrimitiveKind::Float32: {
140
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
141
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
142
+ return env.Null();
143
+ }
144
+
145
+ float f = CopyNodeNumber<float>(value);
146
+ *(float *)args_ptr = f;
147
+ args_ptr += 4;
148
+ } break;
149
+ case PrimitiveKind::Float64: {
150
+ if (RG_UNLIKELY(!value.IsNumber() && !value.IsBigInt())) {
151
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected number", GetValueType(instance, value), i + 1);
152
+ return env.Null();
153
+ }
154
+
155
+ double d = CopyNodeNumber<double>(value);
156
+ *(double *)args_ptr = d;
157
+ args_ptr += 8;
158
+ } break;
159
+ case PrimitiveKind::String: {
160
+ if (RG_UNLIKELY(!value.IsString())) {
161
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected string", GetValueType(instance, value), i + 1);
162
+ return env.Null();
163
+ }
164
+
165
+ const char *str = CopyNodeString(value, &lib->tmp_alloc);
166
+ *(const char **)args_ptr = str;
167
+ args_ptr += 4;
168
+ } break;
169
+ case PrimitiveKind::Pointer: {
170
+ if (RG_UNLIKELY(!CheckValueTag(instance, value, param.type))) {
171
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected %3", GetValueType(instance, value), i + 1, param.type->name);
172
+ return env.Null();
173
+ }
174
+
175
+ void *ptr = value.As<Napi::External<void>>();
176
+ *(void **)args_ptr = ptr;
177
+ args_ptr += 4;
178
+ } break;
179
+
180
+ case PrimitiveKind::Record: {
181
+ if (RG_UNLIKELY(!value.IsObject())) {
182
+ ThrowError<Napi::TypeError>(env, "Unexpected %1 value for argument %2, expected object", GetValueType(instance, value), i + 1);
183
+ return env.Null();
184
+ }
185
+
186
+ Napi::Object obj = value.As<Napi::Object>();
187
+
188
+ args_ptr = AlignUp(args_ptr, param.type->align);
189
+ if (!PushObject(obj, param.type, &lib->tmp_alloc, args_ptr))
190
+ return env.Null();
191
+ args_ptr += param.type->size;
192
+ } break;
193
+ }
194
+ }
195
+
196
+ // DumpStack(func, MakeSpan(sp_ptr, top_ptr - sp_ptr));
197
+
198
+ // Execute and convert return value
199
+ switch (func->ret.type->primitive) {
200
+ case PrimitiveKind::Float32: {
201
+ float f = ForwardCallF(func->func, sp_ptr);
202
+
203
+ return Napi::Number::New(env, (double)f);
204
+ } break;
205
+
206
+ case PrimitiveKind::Float64: {
207
+ double d = ForwardCallD(func->func, sp_ptr);
208
+
209
+ return Napi::Number::New(env, d);
210
+ } break;
211
+
212
+ default: {
213
+ // We can't directly use the struct as a return value, because not all platforms
214
+ // treat it the same: it is trivial only on Windows (see AnalyseFunction).
215
+ uint64_t raw = ForwardCallG(func->func, sp_ptr);
216
+ struct {
217
+ uint32_t rax;
218
+ uint32_t rdx;
219
+ } ret;
220
+ memcpy(&ret, &raw, RG_SIZE(raw));
221
+
222
+ switch (func->ret.type->primitive) {
223
+ case PrimitiveKind::Void: return env.Null();
224
+ case PrimitiveKind::Bool: return Napi::Boolean::New(env, ret.rax);
225
+ case PrimitiveKind::Int8: return Napi::Number::New(env, (double)ret.rax);
226
+ case PrimitiveKind::UInt8: return Napi::Number::New(env, (double)ret.rax);
227
+ case PrimitiveKind::Int16: return Napi::Number::New(env, (double)ret.rax);
228
+ case PrimitiveKind::UInt16: return Napi::Number::New(env, (double)ret.rax);
229
+ case PrimitiveKind::Int32: return Napi::Number::New(env, (double)ret.rax);
230
+ case PrimitiveKind::UInt32: return Napi::Number::New(env, (double)ret.rax);
231
+ case PrimitiveKind::Int64: return Napi::BigInt::New(env, (int64_t)raw);
232
+ case PrimitiveKind::UInt64: return Napi::BigInt::New(env, raw);
233
+ case PrimitiveKind::Float32: { RG_UNREACHABLE(); } break;
234
+ case PrimitiveKind::Float64: { RG_UNREACHABLE(); } break;
235
+ case PrimitiveKind::String: return Napi::String::New(env, (const char *)ret.rax);
236
+ case PrimitiveKind::Pointer: {
237
+ void *ptr = (void *)ret.rax;
238
+
239
+ Napi::External<void> external = Napi::External<void>::New(env, ptr);
240
+ SetValueTag(instance, external, func->ret.type);
241
+
242
+ return external;
243
+ } break;
244
+
245
+ case PrimitiveKind::Record: {
246
+ const uint8_t *ptr = return_ptr ? return_ptr : (const uint8_t *)&ret;
247
+ Napi::Object obj = PopObject(env, ptr, func->ret.type);
248
+ return obj;
249
+ } break;
250
+ }
251
+ } break;
252
+ }
253
+
254
+ RG_UNREACHABLE();
255
+ }
256
+
257
+ }
258
+
259
+ #endif
@@ -0,0 +1,48 @@
1
+ // This program is free software: you can redistribute it and/or modify
2
+ // it under the terms of the GNU Affero General Public License as published by
3
+ // the Free Software Foundation, either version 3 of the License, or
4
+ // (at your option) any later version.
5
+ //
6
+ // This program is distributed in the hope that it will be useful,
7
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ // GNU Affero General Public License for more details.
10
+ //
11
+ // You should have received a copy of the GNU Affero General Public License
12
+ // along with this program. If not, see https://www.gnu.org/licenses/.
13
+
14
+ .global ForwardCallG
15
+ .global ForwardCallF
16
+ .global ForwardCallD
17
+
18
+ // Copy function pointer to EAX, in order to save it through argument forwarding.
19
+ // Save ESP in EBX (non-volatile), and use carefully assembled stack provided by caller.
20
+ // Call native function.
21
+ // Once done, restore normal stack pointer and return.
22
+ // The return value is passed back untouched.
23
+ .macro forward
24
+ .cfi_startproc
25
+ .cfi_def_cfa esp, 4
26
+ endbr32
27
+ push %ebx
28
+ .cfi_def_cfa esp, 8
29
+ movl 8(%esp), %eax
30
+ movl %esp, %ebx
31
+ .cfi_def_cfa ebx, 8
32
+ movl 12(%esp), %esp
33
+ call *%eax
34
+ movl %ebx, %esp
35
+ pop %ebx
36
+ .cfi_def_cfa esp, 4
37
+ ret
38
+ .cfi_endproc
39
+ .endm
40
+
41
+ ForwardCallG:
42
+ forward
43
+
44
+ ForwardCallF:
45
+ forward
46
+
47
+ ForwardCallD:
48
+ forward