foreign_function_var.js
3.1 KB
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
/**
* Module dependencies.
*/
var CIF_var = require('./cif_var')
, Type = require('./type')
, _ForeignFunction = require('./_foreign_function')
, assert = require('assert')
, debug = require('debug')('ffi:VariadicForeignFunction')
, ref = require('ref')
, bindings = require('./bindings')
, POINTER_SIZE = ref.sizeof.pointer
, FFI_ARG_SIZE = bindings.FFI_ARG_SIZE
/**
* For when you want to call to a C function with variable amount of arguments.
* i.e. `printf()`.
*
* This function takes care of caching and reusing ForeignFunction instances that
* contain the same ffi_type argument signature.
*/
function VariadicForeignFunction (funcPtr, returnType, fixedArgTypes, abi) {
debug('creating new VariadicForeignFunction', funcPtr)
// the cache of ForeignFunction instances that this
// VariadicForeignFunction has created so far
var cache = {}
// check args
assert(Buffer.isBuffer(funcPtr), 'expected Buffer as first argument')
assert(!!returnType, 'expected a return "type" object as the second argument')
assert(Array.isArray(fixedArgTypes), 'expected Array of arg "type" objects as the third argument')
var numFixedArgs = fixedArgTypes.length
// normalize the "types" (they could be strings,
// so turn into real type instances)
fixedArgTypes = fixedArgTypes.map(ref.coerceType)
// get the names of the fixed arg types
var fixedKey = fixedArgTypes.map(function (type) {
return getId(type)
})
// what gets returned is another function that needs to be invoked with the rest
// of the variadic types that are being invoked from the function.
function variadic_function_generator () {
debug('variadic_function_generator invoked')
// first get the types of variadic args we are working with
var argTypes = fixedArgTypes.slice()
var key = fixedKey.slice()
for (var i = 0; i < arguments.length; i++) {
var type = ref.coerceType(arguments[i])
argTypes.push(type)
var ffi_type = Type(type)
assert(ffi_type.name)
key.push(getId(type))
}
// now figure out the return type
var rtnType = ref.coerceType(variadic_function_generator.returnType)
var rtnName = getId(rtnType)
assert(rtnName)
// first let's generate the key and see if we got a cache-hit
key = rtnName + key.join('')
var func = cache[key]
if (func) {
debug('cache hit for key:', key)
} else {
// create the `ffi_cif *` instance
debug('creating the variadic ffi_cif instance for key:', key)
var cif = CIF_var(returnType, argTypes, numFixedArgs, abi)
func = cache[key] = _ForeignFunction(cif, funcPtr, rtnType, argTypes)
}
return func
}
// set the return type. we set it as a property of the function generator to
// allow for monkey patching the return value in the very rare case where the
// return type is variadic as well
variadic_function_generator.returnType = returnType
return variadic_function_generator
}
module.exports = VariadicForeignFunction
var idKey = '_ffiId'
function getId (type) {
if (!type.hasOwnProperty(idKey)) {
type[idKey] = (((1+Math.random())*0x10000)|0).toString(16)
}
return type[idKey]
}