/
foreign_function.js
109 lines (91 loc) · 2.83 KB
/
foreign_function.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
var ffi = require('./ffi')
, EventEmitter = require('events').EventEmitter
, POINTER_SIZE = ffi.Bindings.POINTER_SIZE
/**
* Represents a foreign function in another library. Manages all of the aspects
* of function execution, including marshalling the data parameters for the
* function into native types and also unmarshalling the return from function
* execution.
*/
function ForeignFunction (ptr, returnType, types, async) {
if (!(this instanceof ForeignFunction)) {
return new ForeignFunction(ptr, returnType, types, async)
}
var self = this
, numArgs = types.length
, drefVal = ffi.derefValuePtrFunc(returnType)
, result = new ffi.Pointer(ffi.sizeOf(returnType))
, argsList = new ffi.Pointer(numArgs * POINTER_SIZE)
, cif = new ffi.CIF(returnType, types)
, caller = new ffi.Bindings.ForeignCaller(
cif.getPointer()
, ptr
, argsList
, result
, async
)
// XXX: Can't remove or shit segsaults... WTF....
this._ = cif
// allocate a storage area for each argument,
// then write the pointer to the argument list
var argputf = types.map(function (type, i) {
var argPtr = argsList.seek(i * POINTER_SIZE)
if (ffi.isStructType(type)) {
return function (val) {
argPtr.putPointer(val.ref())
}
}
var valPtr = new ffi.Pointer(ffi.sizeOf(type))
argPtr.putPointer(valPtr)
if (type == 'string') {
return function (val) {
var ptr = ffi.Pointer.NULL
if (typeof val !== 'undefined' && val !== null) {
var len = Buffer.byteLength(val, 'utf8')
ptr = new ffi.Pointer(len+1)
ptr.putCString(val)
}
valPtr.putPointer(ptr)
}
} else if (type == 'pointer') {
// Bypass the struct check for non-struct types
return function (val) {
valPtr._putPointer(val)
}
} else {
// Generic type putter function
var putCall = 'put' + ffi.TYPE_TO_POINTER_METHOD_MAP[type]
return function (val) {
valPtr[putCall](val)
}
}
})
var proxy = function () {
self // XXX: if this isn't in here, callbacks segfault. what.. the.. f?
if (arguments.length !== numArgs) {
throw new Error('Function arguments did not meet specification')
}
// write arguments to storage areas
for (var i=0; i<numArgs; i++) {
argputf[i](arguments[i])
}
var r = caller.exec()
if (async) {
var emitter = new EventEmitter()
r.on('success', function () {
emitter.emit('success', drefVal(result))
})
return emitter
}
return drefVal(result)
}
// Backwards compat
// XXX: Remove soon...
proxy.getFunction = function () { return this }
return proxy
}
module.exports = ForeignFunction
/**
* Deprecated. Just invoke ForeignFunction() instead.
*/
ForeignFunction.build = ForeignFunction