Skip to content

Commit ce75f84

Browse files
committed
ffi: make FFIFunctionInfo a BaseObject subclass
This brings the typical benefits of using `BaseObject`, such as a reduced risk of memory management pitfalls (e.g. accidental `v8::Global` recursive references) and diagnostic tracking. Signed-off-by: Anna Henningsen <anna@addaleax.net>
1 parent 0eb6042 commit ce75f84

2 files changed

Lines changed: 76 additions & 34 deletions

File tree

src/node_ffi.cc

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ using v8::Boolean;
2020
using v8::Context;
2121
using v8::DontDelete;
2222
using v8::DontEnum;
23-
using v8::External;
2423
using v8::Function;
2524
using v8::FunctionCallbackInfo;
2625
using v8::FunctionTemplate;
@@ -39,11 +38,13 @@ using v8::ReadOnly;
3938
using v8::String;
4039
using v8::TryCatch;
4140
using v8::Value;
42-
using v8::WeakCallbackInfo;
43-
using v8::WeakCallbackType;
4441

4542
namespace ffi {
4643

44+
void FFIFunctionInfo::MemoryInfo(MemoryTracker* tracker) const {
45+
tracker->TrackField("sb_backing", sb_backing);
46+
}
47+
4748
DynamicLibrary::DynamicLibrary(Environment* env, Local<Object> object)
4849
: BaseObject(env, object), lib_{}, handle_(nullptr), symbols_() {
4950
MakeWeak();
@@ -189,13 +190,44 @@ Maybe<DynamicLibrary::PreparedFunction> DynamicLibrary::PrepareFunction(
189190
return Just(PreparedFunction{fn, should_cache_symbol, should_cache_function});
190191
}
191192

192-
void DynamicLibrary::CleanupFunctionInfo(
193-
const WeakCallbackInfo<FFIFunctionInfo>& data) {
194-
FFIFunctionInfo* info = data.GetParameter();
195-
info->fn.reset();
196-
info->self.Reset();
197-
info->library.Reset();
198-
delete info;
193+
FFIFunctionInfo::FFIFunctionInfo(Environment* env,
194+
Local<Object> object,
195+
std::shared_ptr<FFIFunction> fn,
196+
DynamicLibrary* library)
197+
: BaseObject(env, object), fn(std::move(fn)) {
198+
// Keep the DynamicLibrary instance alive as long as any of its functions are
199+
// alive
200+
object->SetInternalField(FFIFunctionInfo::kLibrary, library->object());
201+
}
202+
203+
Local<FunctionTemplate> FFIFunctionInfo::GetConstructorTemplate(
204+
IsolateData* isolate_data) {
205+
Local<FunctionTemplate> tmpl =
206+
isolate_data->ffi_function_constructor_template();
207+
if (tmpl.IsEmpty()) {
208+
Isolate* isolate = isolate_data->isolate();
209+
tmpl = MakeLazilyInitializedJSTemplate(isolate_data, kInternalFieldCount);
210+
Local<String> classname = FIXED_ONE_BYTE_STRING(isolate, "FFIFunctionInfo");
211+
tmpl->SetClassName(classname);
212+
auto instance = tmpl->InstanceTemplate();
213+
instance->SetInternalFieldCount(FFIFunctionInfo::kInternalFieldCount);
214+
isolate_data->set_ffi_function_constructor_template(tmpl);
215+
}
216+
return tmpl;
217+
}
218+
219+
BaseObjectPtr<FFIFunctionInfo> FFIFunctionInfo::Create(
220+
Environment* env,
221+
std::shared_ptr<FFIFunction> fn,
222+
DynamicLibrary* library) {
223+
Local<Object> obj;
224+
if (!GetConstructorTemplate(env->isolate_data())
225+
->InstanceTemplate()
226+
->NewInstance(env->context())
227+
.ToLocal(&obj)) {
228+
return nullptr;
229+
}
230+
return MakeWeakBaseObject<FFIFunctionInfo>(env, obj, std::move(fn), library);
199231
}
200232

201233
MaybeLocal<Function> DynamicLibrary::CreateFunction(
@@ -205,24 +237,18 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
205237
Isolate* isolate = env->isolate();
206238
Local<Context> context = env->context();
207239

208-
// The info is held in a unique_ptr so early-return paths free it. Ownership
209-
// moves to the weak callback via `release()` once `SetWeak` succeeds.
210-
auto info = std::make_unique<FFIFunctionInfo>();
211-
info->fn = fn;
240+
auto info = FFIFunctionInfo::Create(env, fn, this);
212241

213242
DCHECK_EQ(fn->args.size(), fn->arg_type_names.size());
214243

215244
bool use_sb = IsSBEligibleSignature(*fn);
216245
bool has_ptr_args = use_sb && SignatureHasPointerArgs(*fn);
217246

218-
Local<External> data =
219-
External::New(isolate, info.get(), v8::kExternalPointerTypeTagDefault);
220-
221247
MaybeLocal<Function> maybe_ret =
222248
Function::New(context,
223249
use_sb ? DynamicLibrary::InvokeFunctionSB
224250
: DynamicLibrary::InvokeFunction,
225-
data);
251+
info->object());
226252
Local<Function> ret;
227253
if (!maybe_ret.ToLocal(&ret)) {
228254
return MaybeLocal<Function>();
@@ -270,7 +296,8 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
270296
// (strings, Buffers, ArrayBuffers, and ArrayBufferViews).
271297
if (has_ptr_args) {
272298
Local<Function> slow_fn;
273-
if (!Function::New(context, DynamicLibrary::InvokeFunction, data)
299+
if (!Function::New(
300+
context, DynamicLibrary::InvokeFunction, info->object())
274301
.ToLocal(&slow_fn)) {
275302
return MaybeLocal<Function>();
276303
}
@@ -313,12 +340,6 @@ MaybeLocal<Function> DynamicLibrary::CreateFunction(
313340
}
314341
}
315342

316-
info->self.Reset(isolate, ret);
317-
info->library.Reset(isolate, object());
318-
info->self.SetWeak(info.release(),
319-
DynamicLibrary::CleanupFunctionInfo,
320-
WeakCallbackType::kParameter);
321-
322343
return ret;
323344
}
324345

@@ -375,8 +396,8 @@ void DynamicLibrary::Close(const FunctionCallbackInfo<Value>& args) {
375396

376397
void DynamicLibrary::InvokeFunction(const FunctionCallbackInfo<Value>& args) {
377398
Environment* env = Environment::GetCurrent(args);
378-
FFIFunctionInfo* info = static_cast<FFIFunctionInfo*>(
379-
args.Data().As<External>()->Value(v8::kExternalPointerTypeTagDefault));
399+
FFIFunctionInfo* info = Unwrap<FFIFunctionInfo>(args.Data());
400+
CHECK_NOT_NULL(info);
380401
FFIFunction* fn = info->fn.get();
381402

382403
if (fn == nullptr || fn->closed || fn->ptr == nullptr) {
@@ -444,8 +465,8 @@ void DynamicLibrary::InvokeFunction(const FunctionCallbackInfo<Value>& args) {
444465

445466
void DynamicLibrary::InvokeFunctionSB(const FunctionCallbackInfo<Value>& args) {
446467
Environment* env = Environment::GetCurrent(args);
447-
FFIFunctionInfo* info =
448-
static_cast<FFIFunctionInfo*>(args.Data().As<External>()->Value());
468+
FFIFunctionInfo* info = Unwrap<FFIFunctionInfo>(args.Data());
469+
CHECK_NOT_NULL(info);
449470
FFIFunction* fn = info->fn.get();
450471

451472
if (fn == nullptr || fn->closed || fn->ptr == nullptr) {
@@ -920,7 +941,7 @@ void DynamicLibrary::RegisterCallback(const FunctionCallbackInfo<Value>& args) {
920941
status = ffi_prep_closure_loc(callback->closure,
921942
&callback->cif,
922943
DynamicLibrary::InvokeCallback,
923-
callback,
944+
callback.get(),
924945
callback->ptr);
925946
if (status != FFI_OK) {
926947
const char* msg = "ffi_prep_closure_loc failed";

src/node_ffi.h

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,33 @@ struct FFIFunction {
2929
std::string return_type_name;
3030
};
3131

32-
struct FFIFunctionInfo {
32+
class FFIFunctionInfo final : public BaseObject {
33+
public:
34+
enum InternalFields {
35+
kLibrary = BaseObject::kInternalFieldCount,
36+
kInternalFieldCount
37+
};
38+
39+
FFIFunctionInfo(Environment* env,
40+
v8::Local<v8::Object> object,
41+
std::shared_ptr<FFIFunction> fn,
42+
DynamicLibrary* library);
43+
44+
void MemoryInfo(MemoryTracker* tracker) const override;
45+
SET_MEMORY_INFO_NAME(FFIFunctionInfo)
46+
SET_SELF_SIZE(FFIFunctionInfo)
47+
48+
static BaseObjectPtr<FFIFunctionInfo> Create(Environment* env,
49+
std::shared_ptr<FFIFunction> fn,
50+
DynamicLibrary* library);
51+
static v8::Local<v8::FunctionTemplate> GetConstructorTemplate(
52+
IsolateData* isolate_data);
53+
54+
private:
3355
std::shared_ptr<FFIFunction> fn;
34-
v8::Global<v8::Function> self;
3556
std::shared_ptr<v8::BackingStore> sb_backing;
36-
// Keep the owning DynamicLibrary alive while the generated function is alive.
37-
v8::Global<v8::Object> library;
57+
58+
friend class DynamicLibrary;
3859
};
3960

4061
struct FFICallback {

0 commit comments

Comments
 (0)