Skip to content

Commit a24ea22

Browse files
committed
fixup! src,lib: initial ffi implementation
1 parent bd1b90b commit a24ea22

6 files changed

Lines changed: 125 additions & 60 deletions

File tree

doc/api/errors.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,38 @@ added: v14.0.0
12911291
Used when a feature that is not available
12921292
to the current platform which is running Node.js is used.
12931293

1294+
<a id="ERR_FFI_LIBRARY_LOAD_FAILED"></a>
1295+
1296+
### `ERR_FFI_LIBRARY_LOAD_FAILED`
1297+
1298+
<!--
1299+
added: REPLACEME
1300+
-->
1301+
1302+
The loading of a shared library failed in [`ffi.getNativeFunction()`][].
1303+
1304+
<a id="ERR_FFI_SYMBOL_NOT_FOUND"></a>
1305+
1306+
### `ERR_FFI_SYMBOL_NOT_FOUND`
1307+
1308+
<!--
1309+
added: REPLACEME
1310+
-->
1311+
1312+
The given function symbol was not found in the given shared library in
1313+
[`ffi.getNativeFunction()`][].
1314+
1315+
<a id="ERR_FFI_UNSUPPORTED_TYPE"></a>
1316+
1317+
### `ERR_FFI_UNSUPPORTED_TYPE`
1318+
1319+
<!--
1320+
added: REPLACEME
1321+
-->
1322+
1323+
One or more of the types passed to [`ffi.getNativeFunction()`][] as return or
1324+
argument types is not supported.
1325+
12941326
<a id="ERR_FS_CP_DIR_TO_NON_DIR"></a>
12951327

12961328
### `ERR_FS_CP_DIR_TO_NON_DIR`
@@ -3582,6 +3614,7 @@ The native call from `process.cpuUsage` could not be processed.
35823614
[`dgram.disconnect()`]: dgram.md#socketdisconnect
35833615
[`dgram.remoteAddress()`]: dgram.md#socketremoteaddress
35843616
[`errno`(3) man page]: https://man7.org/linux/man-pages/man3/errno.3.html
3617+
[`ffi.getNativeFunction()`]: ffi.md#ffigetnativefunctionlibrary-func-retType-argtypes
35853618
[`fs.Dir`]: fs.md#class-fsdir
35863619
[`fs.cp()`]: fs.md#fscpsrc-dest-options-callback
35873620
[`fs.readFileSync`]: fs.md#fsreadfilesyncpath-options

lib/ffi.js

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const {
44
BigInt,
55
DataView,
66
Symbol,
7-
Uint8Array,
87
} = primordials;
98

109
const {
@@ -16,8 +15,21 @@ const {
1615
getLibrary,
1716
types,
1817
sizes,
18+
charIsSigned
1919
} = internalBinding('ffi');
2020

21+
const {
22+
isArrayBufferView,
23+
isAnyArrayBuffer,
24+
} = require('internal/util/types');
25+
26+
const {
27+
ERR_FFI_LIBRARY_LOAD_FAILED,
28+
ERR_FFI_SYMBOL_NOT_FOUND,
29+
ERR_FFI_UNSUPPORTED_TYPE,
30+
ERR_INVALID_ARG_TYPE,
31+
} = require('internal/errors');
32+
2133
// [ sigPtr | fnPtr | rvalue | avaluesPointers ]
2234
const callBuffer = new ArrayBuffer(4096);
2335
const callBufferPtr = setCallBuffer(callBuffer);
@@ -28,9 +40,9 @@ const libCache = {};
2840
const POINTER_SIZE = sizes['char *'];
2941
const NULL = Symbol('null');
3042

31-
function normalizePath(lib) {
32-
// TODO do we really need to do anything here?
33-
return lib;
43+
function isSigned(type) {
44+
if (type === 'char') return charIsSigned;
45+
return !type.includes('unsigned') && !type.startsWith('uint');
3446
}
3547

3648
const typesToFfiTypes = {
@@ -76,7 +88,7 @@ function normalizeTypeToFfiTypes(type) {
7688
return `${signed ? 'u' : ''}int${size * 8}`;
7789
}
7890
if (typesToFfiTypes[type]) return typesToFfiTypes[type];
79-
throw new TypeError(`Type "${type}" unsupported`);
91+
throw new ERR_FFI_UNSUPPORTED_TYPE(type);
8092
}
8193

8294
const writers = {
@@ -126,7 +138,7 @@ writers.pointer = writers.uint64_t;
126138
function getWriter(type) {
127139
if (writers[type]) return writers[type];
128140
if (type.includes('*')) return writers.pointer;
129-
const signed = !type.includes('unsigned');
141+
const signed = isSigned(type);
130142
const size = sizeof(type);
131143
return writers[`${signed ? '' : 'u'}int${size * 8}_t`];
132144
}
@@ -149,13 +161,15 @@ readers.pointer = readers.uint64_t;
149161
function getReader(type) {
150162
if (readers[type]) return readers[type];
151163
if (type.includes('*')) return readers.pointer;
152-
const signed = type.includes('unsigned');
164+
const signed = isSigned(type);
153165
const size = sizeof(type);
154166
return readers[`${signed ? '' : 'u'}int${size * 8}_t`];
155167
}
156168

157169
function getNativeFunction(lib, funcName, ret, args) {
158-
lib = normalizePath(lib);
170+
if (lib !== null && typeof lib !== 'string') {
171+
throw new ERR_INVALID_ARG_TYPE('library', ['string', 'null'], lib);
172+
}
159173
if (lib === null) {
160174
lib = NULL;
161175
}
@@ -166,19 +180,23 @@ function getNativeFunction(lib, funcName, ret, args) {
166180

167181
let libPtr = libCache[lib];
168182
if (!libCache[lib]) {
169-
libPtr = libCache[lib] = getLibrary(lib === NULL ? null : lib);
183+
libPtr = getLibrary(lib === NULL ? null : lib);
184+
if (!libPtr) {
185+
throw new ERR_FFI_LIBRARY_LOAD_FAILED(lib === null ? 'null' : lib);
186+
}
187+
libCache[lib] = libPtr;
170188
}
171189

172190
const funcPtr = getSymbol(libPtr, funcName);
173191
if (funcPtr === 0n) {
174-
throw new Error(`Symbol "${funcName}" not found`);
192+
throw new ERR_FFI_SYMBOL_NOT_FOUND(funcName, lib === null ? 'null' : lib);
175193
}
176194

177195
const sig = new FfiSignature(funcPtr, types[ret], args.map((n) => types[n]));
178196

179197
return function(...callArgs) {
180198
let offset = 0;
181-
offset = writers.pointer(offset, sig.pointer); // TODO is this ref enough to keep sig alive?
199+
offset = writers.pointer(offset, sig.pointer);
182200
offset += POINTER_SIZE; // for the return value
183201
let argsOffset = offset + (args.length * POINTER_SIZE);
184202
for (let i = 0; i < args.length; i++) {
@@ -192,11 +210,19 @@ function getNativeFunction(lib, funcName, ret, args) {
192210

193211
function getBufferPointer(buf) {
194212
let offset = 0n;
195-
if (buf instanceof Uint8Array) { // TODO what's the Node.js internals way to do this?
213+
if (isArrayBufferView(buf)) {
196214
buf = buf.buffer;
197-
offset = BigInt(buf.offset || 0);
215+
offset = BigInt(buf.byteOffset || 0);
216+
}
217+
if (!isAnyArrayBuffer(buf)) {
218+
throw new ERR_INVALID_ARG_TYPE('buffer', [
219+
'Buffer',
220+
'TypedArray',
221+
'DataView',
222+
'ArrayBuffer',
223+
'SharedArrayBuffer',
224+
], buf);
198225
}
199-
// TODO assert it's an ArrayBuffer
200226
const ptr = getBufferPointerInternal(buf);
201227
return ptr + offset;
202228
}

lib/internal/errors.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,15 @@ E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM',
10481048
'The feature %s is unavailable on the current platform' +
10491049
', which is being used to run Node.js',
10501050
TypeError);
1051+
E('ERR_FFI_LIBRARY_LOAD_FAILED',
1052+
'Failed to load shared library "%s"',
1053+
Error);
1054+
E('ERR_FFI_SYMBOL_NOT_FOUND',
1055+
'Symbol "%s" not found in library "%s"',
1056+
Error);
1057+
E('ERR_FFI_UNSUPPORTED_TYPE',
1058+
'Type "%s" not supported for FFI',
1059+
TypeError);
10511060
E('ERR_FS_CP_DIR_TO_NON_DIR',
10521061
'Cannot overwrite directory with non-directory', SystemError);
10531062
E('ERR_FS_CP_EEXIST', 'Target already exists', SystemError);

src/node_binding.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class DLib {
7777
#endif
7878

7979
DLib(const char* filename, int flags);
80-
DLib(int flags);
80+
explicit DLib(int flags);
8181

8282
bool Open();
8383
void Close();

src/node_ffi.cc

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
#include "node_ffi.h"
22
#include "env-inl.h"
3-
#include "env.h"
43
#include "ffi.h"
54
#include "node_binding.h"
6-
#include "util.h"
75
#include "v8-array-buffer.h"
86
#include "v8-function-callback.h"
97
#include "v8-primitive.h"
@@ -12,17 +10,15 @@
1210
#include "v8-fast-api-calls.h"
1311
#include "v8-template.h"
1412

15-
using v8::Context;
13+
using v8::Array;
1614
using v8::ArrayBuffer;
1715
using v8::BigInt;
18-
using v8::Number;
19-
using v8::Array;
20-
using v8::Isolate;
21-
using v8::String;
16+
using v8::Boolean;
17+
using v8::Context;
2218
using v8::FunctionCallbackInfo;
2319
using v8::FunctionTemplate;
24-
using v8::FastApiCallbackOptions;
25-
using v8::CFunction;
20+
using v8::Isolate;
21+
using v8::Number;
2622

2723
namespace node {
2824
namespace ffi {
@@ -36,7 +32,7 @@ void GetLibrary(const FunctionCallbackInfo<Value>& args) {
3632
Environment* env = Environment::GetCurrent(args);
3733

3834
bool isNull = true;
39-
std::string fname = ""; // TODO(bengl) not this hacky thing
35+
std::string fname = "";
4036
if (!args[0]->IsNull()) {
4137
CHECK(args[0]->IsString());
4238
node::Utf8Value filename(env->isolate(), args[0]);
@@ -55,11 +51,8 @@ void GetLibrary(const FunctionCallbackInfo<Value>& args) {
5551
} else {
5652
lib = new binding::DLib(fname.c_str(), binding::DLib::kDefaultFlags);
5753
}
54+
if (!lib->Open()) return;
5855
libraries[fname] = lib;
59-
bool success = lib->Open();
60-
if (!success) {
61-
// TODO(bengl) what now?
62-
}
6356
}
6457
ReturnBigInt(env->isolate(), lib);
6558
}
@@ -122,10 +115,6 @@ FfiSignature::~FfiSignature() {
122115
free(argvTypes_);
123116
}
124117

125-
void FfiSignature::MemoryInfo(MemoryTracker* tracker) const {
126-
// TODO(bengl)
127-
}
128-
129118
void FfiSignature::New(const FunctionCallbackInfo<Value>& args) {
130119
CHECK(args.IsConstructCall());
131120
CHECK(args[0]->IsBigInt());
@@ -135,7 +124,11 @@ void FfiSignature::New(const FunctionCallbackInfo<Value>& args) {
135124
Environment * env = Environment::GetCurrent(isolate);
136125

137126
FfiSignature* sig = new FfiSignature(
138-
env, args.This(), args[0].As<BigInt>(), args[1].As<BigInt>(), args[2].As<Array>());
127+
env,
128+
args.This(),
129+
args[0].As<BigInt>(),
130+
args[1].As<BigInt>(),
131+
args[2].As<Array>());
139132

140133
args.This()->Set(
141134
env->context(),
@@ -156,10 +149,6 @@ void MakeCall(const FunctionCallbackInfo<Value> &args) {
156149
reinterpret_cast<void**>(avalues));
157150
}
158151

159-
void FfiBindingData::MemoryInfo(MemoryTracker* tracker) const {
160-
// TODO(bengl) is this needed if not allocating any extra memory in here?
161-
}
162-
163152
void Initialize(
164153
Local<Object> target,
165154
Local<Value> unused,
@@ -178,8 +167,10 @@ void Initialize(
178167
SetMethod(context, target, "getSymbol", GetSymbol);
179168
SetMethod(context, target, "getLibrary", GetLibrary);
180169

181-
Local<FunctionTemplate> tmpl = NewFunctionTemplate(isolate, FfiSignature::New);
182-
tmpl->InstanceTemplate()->SetInternalFieldCount(FfiSignature::kInternalFieldCount);
170+
Local<FunctionTemplate> tmpl =
171+
NewFunctionTemplate(isolate, FfiSignature::New);
172+
tmpl->InstanceTemplate()
173+
->SetInternalFieldCount(FfiSignature::kInternalFieldCount);
183174
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
184175
SetConstructorFunction(context, target, "FfiSignature", tmpl);
185176

@@ -205,7 +196,7 @@ void Initialize(
205196
NODE_FFI_SET_TYPE_CONSTANT(uint, ffi_type_uint);
206197
NODE_FFI_SET_TYPE_CONSTANT(int, ffi_type_sint);
207198
NODE_FFI_SET_TYPE_CONSTANT(ulong, ffi_type_ulong);
208-
NODE_FFI_SET_TYPE_CONSTANT(long, ffi_type_slong);
199+
NODE_FFI_SET_TYPE_CONSTANT(long, ffi_type_slong); // NOLINT(runtime/int)
209200
// NODE_FFI_SET_TYPE_CONSTANT(longdouble, ffi_type_longdouble);
210201
NODE_FFI_SET_TYPE_CONSTANT(pointer, ffi_type_pointer);
211202
target->Set(context, OneByteString(isolate, "types"), types).Check();
@@ -214,7 +205,7 @@ void Initialize(
214205
#define NODE_FFI_SET_SIZE_CONSTANT(typ) sizes->Set(context, \
215206
OneByteString(isolate, #typ), \
216207
Number::New(isolate, sizeof(typ))).Check();
217-
NODE_FFI_SET_SIZE_CONSTANT(char); // TODO(bengl) JS needs to know if signed
208+
NODE_FFI_SET_SIZE_CONSTANT(char);
218209
NODE_FFI_SET_SIZE_CONSTANT(signed char);
219210
NODE_FFI_SET_SIZE_CONSTANT(unsigned char);
220211
NODE_FFI_SET_SIZE_CONSTANT(short); // NOLINT(runtime/int)
@@ -228,18 +219,18 @@ void Initialize(
228219
NODE_FFI_SET_SIZE_CONSTANT(signed int);
229220
NODE_FFI_SET_SIZE_CONSTANT(unsigned);
230221
NODE_FFI_SET_SIZE_CONSTANT(unsigned int);
231-
NODE_FFI_SET_SIZE_CONSTANT(long);
232-
NODE_FFI_SET_SIZE_CONSTANT(long int);
233-
NODE_FFI_SET_SIZE_CONSTANT(signed long);
234-
NODE_FFI_SET_SIZE_CONSTANT(signed long int);
235-
NODE_FFI_SET_SIZE_CONSTANT(unsigned long);
236-
NODE_FFI_SET_SIZE_CONSTANT(unsigned long int);
237-
NODE_FFI_SET_SIZE_CONSTANT(long long);
238-
NODE_FFI_SET_SIZE_CONSTANT(long long int);
239-
NODE_FFI_SET_SIZE_CONSTANT(signed long long);
240-
NODE_FFI_SET_SIZE_CONSTANT(signed long long int);
241-
NODE_FFI_SET_SIZE_CONSTANT(unsigned long long);
242-
NODE_FFI_SET_SIZE_CONSTANT(unsigned long long int);
222+
NODE_FFI_SET_SIZE_CONSTANT(long); // NOLINT(runtime/int)
223+
NODE_FFI_SET_SIZE_CONSTANT(long int); // NOLINT(runtime/int)
224+
NODE_FFI_SET_SIZE_CONSTANT(signed long); // NOLINT(runtime/int)
225+
NODE_FFI_SET_SIZE_CONSTANT(signed long int); // NOLINT(runtime/int)
226+
NODE_FFI_SET_SIZE_CONSTANT(unsigned long); // NOLINT(runtime/int)
227+
NODE_FFI_SET_SIZE_CONSTANT(unsigned long int); // NOLINT(runtime/int)
228+
NODE_FFI_SET_SIZE_CONSTANT(long long); // NOLINT(runtime/int)
229+
NODE_FFI_SET_SIZE_CONSTANT(long long int); // NOLINT(runtime/int)
230+
NODE_FFI_SET_SIZE_CONSTANT(signed long long); // NOLINT(runtime/int)
231+
NODE_FFI_SET_SIZE_CONSTANT(signed long long int); // NOLINT(runtime/int)
232+
NODE_FFI_SET_SIZE_CONSTANT(unsigned long long); // NOLINT(runtime/int)
233+
NODE_FFI_SET_SIZE_CONSTANT(unsigned long long int); // NOLINT(runtime/int)
243234
NODE_FFI_SET_SIZE_CONSTANT(float);
244235
NODE_FFI_SET_SIZE_CONSTANT(double);
245236
// NODE_FFI_SET_SIZE_CONSTANT(long double);
@@ -253,6 +244,13 @@ void Initialize(
253244
NODE_FFI_SET_SIZE_CONSTANT(uint64_t);
254245
NODE_FFI_SET_SIZE_CONSTANT(int64_t);
255246
target->Set(context, OneByteString(isolate, "sizes"), sizes).Check();
247+
248+
char test_char = -1;
249+
Local<Boolean> charIsSigned = Boolean::New(isolate, test_char < 0);
250+
target->Set(
251+
context,
252+
OneByteString(isolate, "charIsSigned"),
253+
charIsSigned).Check();
256254
}
257255

258256
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {

0 commit comments

Comments
 (0)