Skip to content

Commit

Permalink
Added ForeignFunction class to wrap ffi_call() parameters so they don…
Browse files Browse the repository at this point in the history
…'t have to constantly be remarshalled
  • Loading branch information
rbranson committed Nov 19, 2010
1 parent 5a64641 commit 80032e8
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 138 deletions.
34 changes: 34 additions & 0 deletions lib/ffi.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,40 @@ FFI.derefValuePtr = function(type, ptr) {
return FFI.isStructType(type) ? new type(retPtr) : retPtr;
}

// Generates a derefValuePtr for a specific type
FFI.derefValuePtrFunc = function(type) {
if (!FFI.isValidParamType(type)) {
throw new Error("Invalid Type: " + type);
}

var getf = FFI.Pointer.getDispatchTable[FFI.typeNameFor(type)];

if (type == "void") {
return function(ptr) { return null; }
}
else if (type == "string") {
return function(ptr) {
var dptr = ptr.getPointer();

if (dptr.isNull()) {
return null;
}

return getf.call(dptr);
}
}
else if (FFI.isStructType(type)) {
return function(ptr) {
return new type(getf.call(ptr));
}
}
else {
return function(ptr) {
return getf.call(ptr);
}
}
};


FFI.typeNameFor = function(typeDef) {
return FFI.isStructType(typeDef) ? "pointer" : typeDef;
Expand Down
116 changes: 75 additions & 41 deletions lib/foreign_function.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ var ForeignFunction = module.exports = function(ptr, returnType, types, async) {
this._resultPtr = new FFI.Pointer(FFI.sizeOf(this._returnType));
this._paramPtrs = [];
this._strParamPtrs = {};

this._argputf = [];
this._arglistPtr = new FFI.Pointer(types.length * FFI.Bindings.POINTER_SIZE);

var cptr = this._arglistPtr.seek(0);

var cptr = this._arglistPtr.seek(0);
// allocate a storage area for each argument, then write the pointer to the argument list
for (var i = 0, len = types.length; i < len; i++) {
var pptr = new FFI.Pointer(FFI.sizeOf(types[i]));
Expand All @@ -31,70 +31,104 @@ var ForeignFunction = module.exports = function(ptr, returnType, types, async) {
this._strParamPtrs[i] = new FFI.Pointer(FFI.STRING_ARGUMENT_BUFFER_SIZE);
pptr.putPointer(this._strParamPtrs[i]);
}

// Assign the writer functions for each argument
this._argputf[i] = putterFunction(types[i], pptr, this._strParamPtrs[i]);
}

this._cif = new FFI.CIF(returnType, types);
this._initializeProxy();
};

function putterFunction(type, pptrRef, spptrRef) {
if (type == "string") {
return function(val) {
// if our incoming string is larger than our allocation area, increase
// the size of our allocation area
var vlen = val.length;

if ((vlen + 1) > spptrRef.allocated) {
var newptr = new FFI.Pointer(vlen + 1);
spptrRef = newptr;
pptrRef.putPointer(newptr);
}

spptrRef.putCString(val);
}
}
else if (FFI.isStructType(type)) {
// wrap any non-struct-instances with a struct instance
return function(val) {
var what = ("__isStructInstance__" in val) ? val : new type(val);
pptrRef.putPointer(what);
}
}
else {
var putCallRef = FFI.Pointer.putDispatchTable[type];

if (type == "pointer") {
// Bypass the struct check for non-struct types
return function (val) {
pptrRef._putPointer(val);
}
}
else {
return function (val) {
putCallRef.call(pptrRef, val);
}
}
}
}

ForeignFunction.prototype._initializeProxy = function() {
var self = this;
var cifptr = this._cif.getPointer();

// cache these for a bit quicker calls
var stlen = this._types.length;
var ffiCall = FFI.Bindings.call;
var pptrs = this._paramPtrs;
var drefVal = FFI.derefValuePtrFunc(this._returnType);
var async = this._async;
var aputf = this._argputf;
var resPtr = this._resultPtr;
var nullptr = FFI.Pointer.NULL;

var caller = new FFI.Bindings.ForeignCaller(
this._cif.getPointer(),
this._fptr,
this._arglistPtr,
this._resultPtr,
async
);

this._proxy = function() {
var async = self._async;
var ptr = self._cif.getPointer();
var alen = arguments.length,
types = self._types; // XXX: if this isn't in here, callbacks segfault. what.. the.. f?

if (arguments.length != self._types.length)
if (alen != stlen)
throw new Error("Function arguments did not meet specification.");


// write arguments to storage areas
for (var i = 0, len = arguments.length; i < len; i++) {
var t = self._types[i];
var v = arguments[i];

if (v == null) {
self._paramPtrs[i].putPointer(FFI.Pointer.NULL);
}
else {
if (t == "string") {
// if it's a string, we have to write it to our secondary buffers

// if our incoming string is larger than our allocation area, increase
// the size of our allocation area
if ((v.length + 1) > self._strParamPtrs[i].allocated) {
var newptr = new FFI.Pointer(v.length + 1);
self._strParamPtrs[i] = newptr;
self._paramPtrs[i].putPointer(newptr);
}

self._strParamPtrs[i].putCString(v);
self._paramPtrs[i].putPointer(self._strParamPtrs[i]);
}
else if (FFI.isStructType(t)) {
// wrap any non-struct-instances with a struct instance
var what = ("__isStructInstance__" in v) ? v : new t(v);
self._paramPtrs[i].putPointer(what);
}
else {
FFI.Pointer.putDispatchTable[t].apply(self._paramPtrs[i], [v]);
}
}
for (var i = 0; i < alen; i++) {
var v = arguments[i];
aputf[i](arguments[i]);
}
var r = FFI.Bindings.call(cifptr, self._fptr, self._arglistPtr, self._resultPtr, async);

var r = caller.exec();

if (async) {
var emitter = new events.EventEmitter();

r.on("success", function() {
emitter.emit("success", FFI.derefValuePtr(self._returnType, self._resultPtr));
emitter.emit("success", drefVal(resPtr));
});

return emitter;
}

return FFI.derefValuePtr(self._returnType, self._resultPtr);
return drefVal(resPtr);
};
};

Expand Down
2 changes: 1 addition & 1 deletion lib/pointer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Pointer.prototype.attach = function(friend) {
// This wraps _putPointer so it supports direct struct writing
Pointer.prototype.putPointer = function(ptr, seek) {
return this._putPointer(
("__wrappedPointer__" in ptr) ? ptr.__wrappedPointer__ : ptr,
(ptr != null && "__wrappedPointer__" in ptr) ? ptr.__wrappedPointer__ : ptr,
seek
);
};
Expand Down
93 changes: 9 additions & 84 deletions src/ffi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ void FFI::InitializeBindings(Handle<Object> target)
{
Local<Object> o = Object::New();

target->Set(String::New("call"), FunctionTemplate::New(FFICall)->GetFunction());
target->Set(String::New("prepCif"), FunctionTemplate::New(FFIPrepCif)->GetFunction());

target->Set(String::New("strtoul"), FunctionTemplate::New(Strtoul)->GetFunction());
target->Set(String::New("POINTER_SIZE"), Integer::New(sizeof(unsigned char *)));
target->Set(String::New("SIZE_SIZE"), Integer::New(sizeof(size_t))); // DEPRECATED
target->Set(String::New("FFI_TYPE_SIZE"), Integer::New(sizeof(ffi_type)));
Expand Down Expand Up @@ -98,91 +97,16 @@ void FFI::InitializeBindings(Handle<Object> target)
target->Set(String::New("TYPE_SIZE_MAP"), smap);
}

int FFI::AsyncFFICall(eio_req *req)
{
AsyncCallParams *p = (AsyncCallParams *)req->data;
ffi_call(p->cif, p->ptr, p->res, p->args);
return 0;
}

int FFI::FinishAsyncFFICall(eio_req *req)
Handle<Value> FFI::Strtoul(const Arguments &args)
{
AsyncCallParams *p = (AsyncCallParams *)req->data;
Local<Value> argv[1];

argv[0] = Local<Value>::New(String::New("success"));

// emit a success event
Local<Function> emit = Local<Function>::Cast(p->emitter->Get(String::NewSymbol("emit")));
emit->Call(p->emitter, 1, argv);

// unref the event loop (ref'd in FFICall)
ev_unref(EV_DEFAULT_UC);

// dispose of our persistent handle to the EventEmitter object
p->emitter.Dispose();

// free up our memory (allocated in FFICall)
delete p;
HandleScope scope;
Pointer *middle = ObjectWrap::Unwrap<Pointer>(args[1]->ToObject());
char buf[128];
args[0]->ToString()->WriteUtf8(buf);

return 0;
}

Handle<Value> FFI::FFICall(const Arguments& args)
{
if (args.Length() >= 4) {
Pointer *cif = ObjectWrap::Unwrap<Pointer>(args[0]->ToObject());
Pointer *fn = ObjectWrap::Unwrap<Pointer>(args[1]->ToObject());
Pointer *fnargs = ObjectWrap::Unwrap<Pointer>(args[2]->ToObject());
Pointer *res = ObjectWrap::Unwrap<Pointer>(args[3]->ToObject());
bool async = false;

if (args.Length() == 5 && args[4]->IsBoolean() && args[4]->BooleanValue())
async = true;

// printf("FFI::FFICall: ffi_call(%p, %p, %p, %p)\n",
// (ffi_cif *)cif->GetPointer(),
// (void (*)(void))fn->GetPointer(),
// (void *)res->GetPointer(),
// (void **)fnargs->GetPointer());
if (async) {
HandleScope scope;
AsyncCallParams *p = new AsyncCallParams();

// cuter way of doing this?
p->cif = (ffi_cif *)cif->GetPointer();
p->ptr = (void (*)(void))fn->GetPointer();
p->res = (void *)res->GetPointer();
p->args = (void **)fnargs->GetPointer();

// get the events.EventEmitter constructor

Local<Object> global = Context::GetCurrent()->Global();
Local<Object> events = global->Get(String::NewSymbol("process"))->ToObject();
Local<Function> emitterConstructor = Local<Function>::Cast(events->Get(String::NewSymbol("EventEmitter")));

// construct a new process.Promise object
p->emitter = Persistent<Object>::New(emitterConstructor->NewInstance());

ev_ref(EV_DEFAULT_UC);
eio_custom(FFI::AsyncFFICall, EIO_PRI_DEFAULT, FFI::FinishAsyncFFICall, p);

return scope.Close(p->emitter);
}
else {
ffi_call(
(ffi_cif *)cif->GetPointer(),
(void (*)(void))fn->GetPointer(),
(void *)res->GetPointer(),
(void **)fnargs->GetPointer()
);
}
}
else {
return THROW_ERROR_EXCEPTION("Not Enough Parameters");
}
unsigned long val = strtoul(buf, (char **)middle->GetPointer(), args[2]->Int32Value());

return Undefined();
return scope.Close(Integer::NewFromUnsigned(val));
}

Handle<Value> FFI::FFIPrepCif(const Arguments& args)
Expand Down Expand Up @@ -226,4 +150,5 @@ extern "C" void init(Handle<Object> target)
FFI::InitializeBindings(target);
FFI::InitializeStaticFunctions(target);
CallbackInfo::Initialize(target);
ForeignCaller::Initialize(target);
}
28 changes: 25 additions & 3 deletions src/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,34 @@ class FFI : public ObjectWrap {
public:
static void InitializeStaticFunctions(Handle<Object> Target);
static void InitializeBindings(Handle<Object> Target);


protected:
static Handle<Value> FFIPrepCif(const Arguments& args);
static Handle<Value> Strtoul(const Arguments& args);
};

class ForeignCaller : public ObjectWrap {
public:
ForeignCaller();
~ForeignCaller();
static void Initialize(Handle<Object> Target);

protected:
static Handle<Value> New(const Arguments& args);
static Handle<Value> Exec(const Arguments& args);
static int AsyncFFICall(eio_req *req);
static int FinishAsyncFFICall(eio_req *req);
static Handle<Value> FFIPrepCif(const Arguments& args);
static Handle<Value> FFICall(const Arguments& args);

ffi_cif *m_cif;
void (*m_fn)(void);
void *m_res;
void **m_fnargs;

bool m_async;

private:
static Persistent<FunctionTemplate> foreign_caller_template;
static Handle<FunctionTemplate> MakeTemplate();
};

class ThreadedCallbackInvokation;
Expand Down
Loading

0 comments on commit 80032e8

Please sign in to comment.