Skip to content

Commit ea3f311

Browse files
committed
fix wchar_t encoding; variadic function support
1 parent 864087a commit ea3f311

File tree

2 files changed

+87
-30
lines changed

2 files changed

+87
-30
lines changed

index.js

+36-23
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ let wchar_t = null;
7272
try {
7373
wchar_t = require('ref-wchar');
7474
const Iconv = require('iconv').Iconv;
75-
const wchar_set = new Iconv('UTF-8', 'UTF-16'+ref.endianness).convert;
76-
const wchar_get = new Iconv('UTF-16'+ref.endianness, 'UTF-8').convert;
75+
let encoding = ((process.platform === 'win32') ? 'UTF-16' : 'UTF-32') + ref.endianness;
76+
const wchar_set = new Iconv('UTF-8', encoding).convert;
77+
const wchar_get = new Iconv(encoding, 'UTF-8').convert;
7778

7879
// monkey patch broken wchar_t.toString
7980
wchar_t.toString = (buffer) => {
@@ -373,24 +374,27 @@ function DefaultTcc() {
373374
}
374375

375376
/**
376-
* Wrapper for lazy evaluation of a ffi.ForeignFunction.
377-
* This is needed to postpone the creation of a ffi.ForeignFunction
377+
* Wrapper for lazy evaluation of a ffi.ForeignFunction or ffi.VariadicForeignFunction.
378+
* This is needed to postpone the creation of the ffi function
378379
* until we got the real C symbol pointer.
379380
* @param {string|object} restype - known type of `ref.types`
380-
* @param {array} args - array of parameter types
381+
* @param {Array} args - array of parameter types
382+
* @param {boolean=} variadic - indicate a variadic function
381383
* @return {function(ref.ref) : ffi.ForeignFunction}
382384
* @function module:node-tinycc.CFuncType
383385
*/
384-
function CFuncType(restype, args) {
385-
return (pointer) => ffi.ForeignFunction(pointer, restype, args);
386+
function CFuncType(restype, args, variadic) {
387+
return (pointer) => (variadic)
388+
? ffi.VariadicForeignFunction(pointer, restype, args)
389+
: ffi.ForeignFunction(pointer, restype, args);
386390
}
387391

388392
/**
389393
* Internal wrapper for ffi.Callback to distingish a function type in
390394
* `CodeGenerator.bindState`. It also holds a reference of
391395
* the callback to avoid early garbage collection.
392396
* @param {string|object} restype - known type of `ref.types`
393-
* @param {array} args - array of parameter types
397+
* @param {Array} args - array of parameter types
394398
* @param {function} f - callback function
395399
* @constructor module:node-tinycc.FuncSymbol
396400
*/
@@ -427,7 +431,7 @@ function FuncSymbol(restype, args, f) {
427431
* @param {string|function} code - C source as string or a function
428432
* returning the source code string
429433
* @param {string=} forward - optional forward declaration
430-
* @param {array=} symbols - optional array of [type, symbol name]
434+
* @param {Array=} symbols - optional array of [type, symbol name]
431435
* to be autoresolved by the generator
432436
* @return {Declaration}
433437
* @constructor module:node-tinycc.Declaration
@@ -469,7 +473,7 @@ function Declaration(code, forward, symbols) {
469473
* );
470474
* gen.addDeclaration(
471475
* tcc.Declaration(
472-
* 'void func() { printf("Hello World!\n"); }',
476+
* 'void func() { printf("Hello World!\\n"); }',
473477
* 'void func();',
474478
* [[tcc.CFuncType('void', []), 'func']]
475479
* )
@@ -633,7 +637,7 @@ CodeGenerator.prototype.addTopDeclaration = function(decl) {
633637
* corresponding type.
634638
*
635639
* @param {Tcc} state - state to bind to symbols
636-
* @return {object}
640+
* @return {Object}
637641
* @method module:node-tinycc.CodeGenerator#bindState
638642
*/
639643
CodeGenerator.prototype.bindState = function(state) {
@@ -725,19 +729,22 @@ function _restype(type) {
725729

726730
/**
727731
* Helper to create full C function declarations.
728-
* @param restype
729-
* @param name
730-
* @param args
731-
* @param pointer
732+
* @param {string|Object} restype
733+
* @param {string} name
734+
* @param {Array} args
735+
* @param {boolean=} varargs
736+
* @param {boolean=} pointer
732737
* @return {string}
733738
* @private
734739
*/
735-
function _func_decl(restype, name, args, pointer) {
740+
function _func_decl(restype, name, args, varargs, pointer) {
736741
let vars = '';
737742
if (args.length)
738743
vars = (args[0] instanceof Array)
739744
? args.map(([type, varname]) => _var_decl(varname, type)).join(', ')
740745
: args.map((type) => _var_decl('', type)).join(', ');
746+
if (varargs)
747+
vars += ', ...';
741748
return `${_restype(restype)} ` + ((pointer) ? `(*${name})` : name) + `(${vars})`;
742749
}
743750

@@ -755,17 +762,17 @@ function _func_decl(restype, name, args, pointer) {
755762
* gen.addDeclaration(decl);
756763
* ...
757764
* ```
758-
* @param {string|object} restype - known type of `ref.types`
765+
* @param {string|Object} restype - known type of `ref.types`
759766
* @param {string} name - function pointer name
760-
* @param {array} args - array of parameter types
767+
* @param {Array} args - array of parameter types
761768
* @param {function} f - Javascript function
762769
* @return {Declaration}
763770
* @function module:node-tinycc.c_callable
764771
*/
765772
function c_callable(restype, name, args, f) {
766773
return new Declaration(
767774
'',
768-
_func_decl(restype, name, args, true) + ' = 0;',
775+
_func_decl(restype, name, args, false, true) + ' = 0;',
769776
[[new FuncSymbol(restype, args, f), name]]
770777
);
771778
}
@@ -798,19 +805,25 @@ function c_callable(restype, name, args, f) {
798805
* gen.bindState(state);
799806
* console.log(add(23, 42)); // use it
800807
* ```
801-
* @param {string|object} restype - known type of `ref.types`
808+
* @param {string|Object} restype - known type of `ref.types`
802809
* @param {string} name - function pointer name
803-
* @param {array} args - array of [type, parameter name]
810+
* @param {Array} args - array of [type, parameter name]
804811
* @param {string} code - C function body
805812
* @return {func} proxy function
806813
* @function module:node-tinycc.c_function
807814
*/
808815
function c_function(restype, name, args, code) {
809-
let header = _func_decl(restype, name, args);
816+
let last_arg = args.pop();
817+
let varargs = (
818+
last_arg && last_arg === '...'
819+
|| (last_arg instanceof Array && last_arg.length === 1 && last_arg[0] === '...'));
820+
if (last_arg && !varargs)
821+
args.push(last_arg);
822+
let header = _func_decl(restype, name, args, varargs);
810823
let declaration = new Declaration(
811824
`${header}\n{\n${code||''}\n}\n`,
812825
header + ';',
813-
[[CFuncType(restype, args.map(([type, _]) => type)), name]]
826+
[[CFuncType(restype, args.map(([type, _]) => type), varargs), name]]
814827
);
815828
let func = function() {
816829
if (func.declaration.symbols_resolved[name])

test/basic.js

+51-7
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,36 @@ float test2 = 1.23;
165165
gen.bindState(state);
166166
assert.equal(fibonacci(10), 55);
167167
});
168+
it('variadic c_function', function() {
169+
let sum = tcc.c_function('int', 'sum', [['int', 'arg'], '...'],
170+
`
171+
va_list ap;
172+
int argc = 0;
173+
174+
va_start(ap, arg);
175+
for (argc=1; va_arg(ap, int); ++argc);
176+
va_end(ap);
177+
178+
int i;
179+
int argv[argc];
180+
va_start(ap, arg);
181+
argv[0] = arg;
182+
for (i = 1; i < argc; ++i)
183+
argv[i] = va_arg(ap, int);
184+
va_end(ap);
185+
186+
int result = 0;
187+
for (i = 0; i < argc; ++i)
188+
result += argv[i];
189+
return result;
190+
`);
191+
gen.addDeclaration(sum);
192+
gen.addTopDeclaration(tcc.Declaration('#include <stdarg.h>'));
193+
state.compile(gen.code());
194+
state.relocate();
195+
gen.bindState(state);
196+
assert.equal(sum('int', 'int', 'int')(1, 2, 3, 0), 6);
197+
});
168198
it('JS function from C', function(){
169199
let add = function(a, b) { return a+b; };
170200
let fromJS = tcc.c_callable('int', 'add', ['int', 'int'], add);
@@ -250,35 +280,49 @@ float test2 = 1.23;
250280
'void a(struct T (*(*t)))');
251281
});
252282
it('void (*a)()', function() {
253-
assert.equal(tcc._func_decl('void', 'a', [], true), 'void (*a)()');
283+
assert.equal(tcc._func_decl('void', 'a', [], false, true), 'void (*a)()');
254284
});
255285
it('int (*a)(int, int)', function() {
256-
assert.equal(tcc._func_decl('int', 'a', ['int', 'int'], true), 'int (*a)(int , int )');
286+
assert.equal(tcc._func_decl('int', 'a', ['int', 'int'], false, true), 'int (*a)(int , int )');
257287
});
258288
it('int* (*a)(char*)', function() {
259-
assert.equal(tcc._func_decl('int*', 'a', ['char*'], true), 'int* (*a)(char (*))');
289+
assert.equal(tcc._func_decl('int*', 'a', ['char*'], false, true), 'int* (*a)(char (*))');
260290
});
261291
it('int** (*a)(char**, char**)', function() {
262-
assert.equal(tcc._func_decl('int**', 'a', ['char**', 'char**'], true),
292+
assert.equal(tcc._func_decl('int**', 'a', ['char**', 'char**'], false, true),
263293
'int** (*a)(char (*(*)), char (*(*)))');
264294
});
265295
it('struct T* (*a)(struct T)', function() {
266296
let T = tcc.c_struct('T', StructType());
267-
assert.equal(tcc._func_decl(ref.refType(T), 'a', [T], true),
297+
assert.equal(tcc._func_decl(ref.refType(T), 'a', [T], false, true),
268298
'struct T* (*a)(struct T )');
269299
});
270300
it('void (*a)(struct T**)', function() {
271301
let T = tcc.c_struct('T', StructType());
272-
assert.equal(tcc._func_decl('void', 'a', [ref.refType(ref.refType(T))], true),
302+
assert.equal(tcc._func_decl('void', 'a', [ref.refType(ref.refType(T))], false, true),
273303
'void (*a)(struct T (*(*)))');
274304
});
275305
it('struct T** (*test)(struct T *(*[5])[10])', function() {
276306
let T = tcc.c_struct('T', StructType({a: 'int'}));
277307
let A1 = ArrayType(ref.refType(T), 10);
278308
let A2 = ArrayType(ref.refType(A1), 5);
279-
assert.equal(tcc._func_decl(ref.refType(ref.refType(T)), 'test', [A2], true),
309+
assert.equal(tcc._func_decl(ref.refType(ref.refType(T)), 'test', [A2], false, true),
280310
'struct T** (*test)(struct T (*((*([5]))[10])))');
281311
});
312+
it('struct T** test(struct T *(*[5])[10], ...)', function() {
313+
let T = tcc.c_struct('T', StructType({a: 'int'}));
314+
let A1 = ArrayType(ref.refType(T), 10);
315+
let A2 = ArrayType(ref.refType(A1), 5);
316+
assert.equal(tcc._func_decl(ref.refType(ref.refType(T)), 'test', [A2], true, false),
317+
'struct T** test(struct T (*((*([5]))[10])), ...)');
318+
});
319+
it('struct T** (*test)(struct T *(*[5])[10], ...)', function() {
320+
let T = tcc.c_struct('T', StructType({a: 'int'}));
321+
let A1 = ArrayType(ref.refType(T), 10);
322+
let A2 = ArrayType(ref.refType(A1), 5);
323+
assert.equal(tcc._func_decl(ref.refType(ref.refType(T)), 'test', [A2], true, true),
324+
'struct T** (*test)(struct T (*((*([5]))[10])), ...)');
325+
});
282326
});
283327
describe('struct tests', function() {
284328
let state;

0 commit comments

Comments
 (0)