Tiny C Compiler binding for nodejs.
With this module it is possible to declare inline C code in nodejs and run it on the fly. This is possible due to the fascinating Tiny C Compiler originally made by Fabrice Bellard.
Requires: module:node-gyp
, module:ffi
, module:ref
Note: The module is still alpha, interfaces are still likely to change a lot.
Example
const tcc = require('node-tinycc');
// create a code generator
let gen = tcc.CodeGenerator();
// create a compile state
let state = tcc.DefaultTcc();
// declare a C function
let c_func = tcc.c_function(
'int', // return type
'add', // function name in C
[['int', 'a'], ['int', 'b']], // parameters as [type, name]
'return a + b + js_func(a, b);' // actual code
);
gen.addDeclaration(c_func);
// add a JS function declaration to C
let js_func = tcc.c_callable(
'int', // return type
'js_func', // function name in C
['int', 'int'], // parameter types
(a, b) => {return a * b;} // function
);
gen.addDeclaration(js_func);
// compile code and relocate
state.compile(gen.code());
state.relocate();
// resolve symbols between C and JS
gen.bindState(state);
// now the C stuff is usable
console.log(c_func(23, 42)); // --> prints 1031
- node-tinycc
- .Tcc
- new Tcc(tcclib)
- .setOptions(option)
- .setLibPath(path)
- .defineSymbol(symbol, [value])
- .undefineSymbol(symbol)
- .addIncludePath(path)
- .addLibrary(library)
- .addLibraryPath(path)
- .addFile(path)
- .compile(code)
- .relocate()
- .addSymbol(symbol, value)
- .getSymbol(symbol) ⇒
ref.refType
|null
- .resolveSymbol(symbol, type) ⇒
*
- .setSymbol(symbol, value)
- .getFunction(symbol, restype, args) ⇒
ffi.ForeignFunction
- .setFunction(symbol, cb)
- .FuncSymbol
- .Declaration
- .CodeGenerator
- new CodeGenerator()
- .loadBasicTypes()
- .code() ⇒
string
- .codeWithLineNumbers() ⇒
string
- .addDeclaration(decl)
- .addTopDeclaration(decl)
- .bindState(state) ⇒
Object
- .WCString(s) ⇒
WCString
- .escapeWchar(s) ⇒
string
- .DefaultTcc() ⇒
Tcc
- .CFuncType(restype, args, [variadic]) ⇒
function
- .c_callable(restype, name, args, f) ⇒
Declaration
- .c_function(restype, name, args, code) ⇒
func
- .c_struct(name, structType) ⇒
StructType
- .Tcc
The Tcc class provides low level access to the libtcc-API of the Tiny C Compiler (TCC).
On Windows this class is constructed in Javascript
with ffi
from a precompiled libtcc.dll delivered with the module.
On POSIX systems the class is a C++ class build in a native extension
from the repository source.
Kind: static class of node-tinycc
Note: It is important to note that you must not mix different TCC states.
Because TCC uses global states internally, any new state will leave
the old one corrupted. The compiled result is not affected by this,
therefore it is important to finish a state up to the compilation
before using a new one. This is a major drawback of the TCC API.
Because of the global internal states it is also not possible to cleanup
a state properly (a Tcc() invocation will leak memory).
While this works:
let state1 = Tcc();
...
state1.compile('...') && state1.relocate(); // finished with state1
let state2 = Tcc(); // state1 got corrupted but we are with it anyways
...
state2.compile('...') && state2.relocate(); // finished with state2
// use symbols from state1 & state2
this will break:
let state1 = Tcc();
let state2 = Tcc(); // state1 got corrupted, state2 is working as expected
- .Tcc
- new Tcc(tcclib)
- .setOptions(option)
- .setLibPath(path)
- .defineSymbol(symbol, [value])
- .undefineSymbol(symbol)
- .addIncludePath(path)
- .addLibrary(library)
- .addLibraryPath(path)
- .addFile(path)
- .compile(code)
- .relocate()
- .addSymbol(symbol, value)
- .getSymbol(symbol) ⇒
ref.refType
|null
- .resolveSymbol(symbol, type) ⇒
*
- .setSymbol(symbol, value)
- .getFunction(symbol, restype, args) ⇒
ffi.ForeignFunction
- .setFunction(symbol, cb)
Param |
---|
tcclib |
Set command line options of TCC. Run tcc -hh
to see known options.
Kind: instance method of Tcc
Param | Type |
---|---|
option | string |
Set TCC library path. For DefaultTcc
this is set to the bundled TCC.
Kind: instance method of Tcc
Param | Type |
---|---|
path | string |
Define a preprocessor symbol.
Kind: instance method of Tcc
Param | Type |
---|---|
symbol | string |
[value] | string |
Undefine a preprocessor symbol.
Kind: instance method of Tcc
Param | Type |
---|---|
symbol | string |
Add include path.
Kind: instance method of Tcc
Param | Type |
---|---|
path | string |
Add a library (same name as -l...).
Kind: instance method of Tcc
Param | Type |
---|---|
library | string |
Add a library path. Equivalent to -Lpath option.
Kind: instance method of Tcc
Param | Type |
---|---|
path | string |
Add a file to compilation.
Kind: instance method of Tcc
Fixme: missing filetype parameter
Param | Type |
---|---|
path | string |
Compile source code.
Kind: instance method of Tcc
Param | Type |
---|---|
code | string |
Relocate after compilation. This is needed before resolving any symbols.
Kind: instance method of Tcc
Add a symbol to the compiled program. This is not reliable on all architectures (likely to segfault on ARM). Use with caution.
Kind: instance method of Tcc
Param | Type |
---|---|
symbol | string |
value | ref.refType |
Get a symbol from the program. Returns void pointer or null
of not found.
Kind: instance method of Tcc
Param | Type |
---|---|
symbol | string |
Resolve a C symbol name as type for further usage in Javascript.
Kind: instance method of Tcc
Note: This is done automatically for known symbols
in CodeGenerator.bindState
.
Param | Type | Description |
---|---|---|
symbol | string |
symbol name |
type | string | object |
known type of ref.types |
Low level function to set the value of a C symbol. Since all toplevel symbols are exported as void pointers, the value must be a pointer type. The referenced type of value has to match the C type of the symbol, otherwise arbitrary memory will be overwritten.
Kind: instance method of Tcc
Param | Type | Description |
---|---|---|
symbol | string |
symbol name |
value | ref.refType |
Resolve a C symbol name as function type.
Kind: instance method of Tcc
Param | Type | Description |
---|---|---|
symbol | string |
symbol name |
restype | string | object |
known type of ref.types |
args | array |
array of parameter types |
Set a C function pointer symbol to a Javascript callback.
The callback must be a ffi.Callback
matching the function
pointer type.
Kind: instance method of Tcc
Param | Type | Description |
---|---|---|
symbol | string |
symbol name |
cb | ffi.Callback |
Javascript callback |
Kind: static class of node-tinycc
Internal wrapper for ffi.Callback to distingish a function type in
CodeGenerator.bindState
. It also holds a reference of
the callback to avoid early garbage collection.
Param | Type | Description |
---|---|---|
restype | string | object |
known type of ref.types |
args | Array |
array of parameter types |
f | function |
callback function |
Kind: static class of node-tinycc
Base object for all declarations to be used with CodeGenerator.
If the standard convenient functions do not suit your needs you can
create a customized declaration with this base object and still use
the code generator.
Add the returned declaration to a generator with addDeclaration
.
After the symbols got mapped by CodeGenerator.bindState
you can
access them via the attribute .symbols_resolved
.
Param | Type | Description |
---|---|---|
code | string | function |
C source as string or a function returning the source code string |
[forward] | string |
optional forward declaration |
[symbols] | Array |
optional array of [type, symbol name] to be autoresolved by the generator |
Example
let declaration = new tcc.Declaration(
` // code
int x = 1;
int func_a() { return func_b() + 1; }
int func_b() { return 0; }
`,
` // forward
int func_a();
int func_b();
`,
[ // symbols
['int', 'x'],
[tcc.CFuncType('int', []), 'func_a'],
[tcc.CFuncType('int', []), 'func_b']
]);
Kind: static class of node-tinycc
- .CodeGenerator
- new CodeGenerator()
- .loadBasicTypes()
- .code() ⇒
string
- .codeWithLineNumbers() ⇒
string
- .addDeclaration(decl)
- .addTopDeclaration(decl)
- .bindState(state) ⇒
Object
Code generator for inline C in Javascript.
The code generator creates the final source code by putting together single declarations. The structure of the final source code is the following:
- top section: The section gets not autofilled by the generator.
Use it with
addTopDeclaration
for any early stuff like including header files and such. - forward section: Used by the generator to do forward declarations.
Any content in
Declaration.forward
will end up here. - code section: Used by the generator to place the code definitions.
To add content to the sections call addDeclaration
or
addTopDeclaration
for the top section.
The order of added content is preserved, this is esp. important
of you omit forward declarations.
Example usage:
let gen = tcc.CodeGenerator();
gen.addTopDeclaration(
tcc.Declaration('#include <stdio.h')
);
gen.addDeclaration(
tcc.Declaration(
'void func() { printf("Hello World!\\n"); }',
'void func();',
[[tcc.CFuncType('void', []), 'func']]
)
);
With code()
you can grab the generated code,
codeWithLineNumbers()
might help to debug the code
if tcc will not compile it.
If you are done adding declarations it is time to compile everything:
let state = tcc.DefaultTcc();
state.compile(gen.code());
state.relocate();
The final step before we can use the compiled code is to resolve
and bind all defined symbols of the declarations. This is done by calling
bindState
:
let resolved_symbols = gen.bindState(state);
Now we can use the func
symbol:
resolved_symbols.func();
Add declarations for the common types of ref.types
.
Some of the known types of the ref module differ from typical
C naming (e.g. int8
instead of int8_t
).
This function adds additional typedefs to solve naming issues.
It adds the following types:
Type | Typedef of |
---|---|
int8 | int8_t |
int16 | int16_t |
int32 | int32_t |
int64 | int64_t |
uint8 | uint8_t |
uint16 | uint16_t |
uint32 | uint32_t |
uint64 | uint64_t |
Object | void * |
CString | char * |
byte | unsigned char |
uchar | unsigned char |
ushort | unsigned short |
uint | unsigned int |
ulong | unsigned long |
longlong | long long |
ulonglong | unsigned long long |
Furthermore it includes <stddef.h>
, <stdint.h>
and <stdbool.h>
.
If the module ref-wchar
is installed, WCString
is typedef'd as
wchar_t
pointer.
Kind: instance method of CodeGenerator
Get the generated code.
Kind: instance method of CodeGenerator
Get the generated code with leading line numbers. This is useful for limited debugging.
Kind: instance method of CodeGenerator
Add a declaration to the generator.
Kind: instance method of CodeGenerator
Param | Type | Description |
---|---|---|
decl | Declaration |
declaration to be added |
Add a declaration to the top section. forward
and symbols
will be ignored for declarations added to the top section.
Kind: instance method of CodeGenerator
Param | Type | Description |
---|---|---|
decl | Declaration |
declaration to be added |
Resolve symbols between C and Javascript. Call this after compilation and relocation before using any C stuff.
The function traverses all symbol names of the added declarations and tries to attach the given type.
Returns an object with all symbol names mapping to the corresponding type.
Kind: instance method of CodeGenerator
Param | Type | Description |
---|---|---|
state | Tcc |
state to bind to symbols |
Helper function for easy wide string creation.
Kind: static method of node-tinycc
Note: The function is only exported, if the module ref-wchar
is installed.
Param | Type |
---|---|
s | string |
Helper function to escape wide character string literals. This is useful when writing C source code strings directly in Javascript. The function escapes the UTF-8 input to the appropriate wchar_t type.
Kind: static method of node-tinycc
Note: The function is only exported, if the module ref-wchar
is installed.
Param | Type |
---|---|
s | string |
Example
> tcc.escapeWchar('öäü')
'\\xf6\\xe4\\xfc'
> `wchar_t *w = L"${tcc.escapeWchar('öäü')}";`
'wchar_t *w = L"\\xf6\\xe4\\xfc";'
tcc.DefaultTcc() ⇒ Tcc
Helper function to create a compile state with the bundled tcc. The function sets the tcclib and include path to the the platform dependent tcc folders.
Kind: static method of node-tinycc
Wrapper for lazy evaluation of a ffi.ForeignFunction or ffi.VariadicForeignFunction. This is needed to postpone the creation of the ffi function until we got the real C symbol pointer.
Kind: static method of node-tinycc
Param | Type | Description |
---|---|---|
restype | string | object |
known type of ref.types |
args | Array |
array of parameter types |
[variadic] | boolean |
indicate a variadic function |
Convenvient declaration function to import a function symbol from JS to C code.
The function creates a function pointer declaration in C. After
calling CodeGenerator.bindState
the function pointer can be used
in C. Example usage:
let callback = tcc.c_callable('int', 'jsfunc', ['int', 'int'], (a, b) => a+b);
// use somewhere in C code
let decl = tcc.Declaration('int test(int x) { return a * jsfunc(23, 42); }');
gen.addDeclaration(callback);
gen.addDeclaration(decl);
...
Kind: static method of node-tinycc
Param | Type | Description |
---|---|---|
restype | string | Object |
known type of ref.types |
name | string |
function pointer name |
args | Array |
array of parameter types |
f | function |
Javascript function |
Convenient declaration function to create a C function that is usable from Javascript.
The Javascript code:
tcc.c_function('int', 'add', [['int', 'a'], ['int', 'b']], 'return a+b;');
will roughly translate to this C source code:
int add(int a, int b) { return a+b; }
Note that the first 3 arguments of c_function
almost read like the C function header.
Additionally the C function will have a forward declaration to use it from
any other C code within the same compile state.
Returns a proxy function, that automatically resolves to the underlying
C function. The actual declaration object resides under .declaration
.
Full usage example:
let add = tcc.c_function('int', 'add', [['int', 'a'], ['int', 'b']], 'return a+b;');
let gen = tcc.CodeGenerator();
gen.addDeclaration(add);
let state = tcc.DefaultTcc();
state.compile(gen.code());
state.relocate();
gen.bindState(state);
console.log(add(23, 42)); // use it
Kind: static method of node-tinycc
Returns: func
- proxy function
Param | Type | Description |
---|---|---|
restype | string | Object |
known type of ref.types |
name | string |
function pointer name |
args | Array |
array of [type, parameter name] |
code | string |
C function body |
Convenient declaration function to declare a struct type usable in C and Javascript.
This function extracts the field names and types of a StructType (module ref-struct
)
to create a struct declaration (forward section) and definition for C (code section).
A field type is resolved recursively to catch complicated type mixtures that
can easily be build with StructTypes, ArrayTypes and pointer types
(e.g. struct XY *(*a[2])[10];
).
No typedef declaration is added for the struct, therefore always reference the
struct type by struct name
in C. The struct type can be used at any point where
a ref.types
type is needed, e.g. as function parameter or return type.
Usage example:
const StructType = require('ref-struct');
let gen = tcc.CodeGenerator();
let S = tcc.c_struct('S', StructType({a: 'int', b: 'char*'}));
addDeclaration(S);
The struct of the example will roughly translate to this C code (beside some more alignment directives):
struct S {
int a;
char (*b);
};
c_struct
finalizes a StructType
, i.e. no more fields can be added afterwards.
To build a struct type with a pointer to itself (e.g. for linked lists),
build the StructType without that field beforehand and use the
StructType.defineProperty
to declare the self pointer member. Decorate the struct
type afterwards with c_struct
:
let S = StructType();
S.defineProperty('self', S);
c_struct('S', S); // defineProperty not allowed after this
Kind: static method of node-tinycc
Returns: StructType
- structType decorated with declaration object
Param | Type | Description |
---|---|---|
name | string |
struct type name in C |
structType | StructType |
structType to be declared in C |