Skip to content

Commit 4834086

Browse files
author
j.breitbart
committed
initial
0 parents  commit 4834086

File tree

4 files changed

+400
-0
lines changed

4 files changed

+400
-0
lines changed

index.js

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
TODO:
3+
- replace ffi shared lib version of Tcc with native version (needed for OSX)
4+
- use EventEmitter for compile steps
5+
- use ref.coerceType where appropriate
6+
- enhance InlineGenerator with struct and array functionality
7+
*/
8+
9+
var ffi = require('ffi');
10+
var ref = require('ref');
11+
12+
var void_p = ref.refType(ref.types.void);
13+
var TCCState_p = void_p;
14+
15+
function Tcc(tcclib, tccpath) {
16+
if (!(this instanceof Tcc))
17+
return new Tcc(tcclib, tccpath);
18+
this.lib = ffi.Library(tcclib || './linux/lib/libtcc.so', {
19+
// missing: tcc_enable_debug, tcc_set_error_func, tcc_set_warning, tcc_add_sysinclude_path, tcc_output_file
20+
'tcc_new': [TCCState_p, []],
21+
'tcc_delete': ['void', [TCCState_p]], // TODO: explicit destructor
22+
'tcc_set_lib_path': ['void', [TCCState_p, 'string']],
23+
'tcc_set_output_type': ['int', [TCCState_p, 'int']],
24+
'tcc_set_options': ['void', [TCCState_p, 'string']],
25+
'tcc_define_symbol': ['void', [TCCState_p, 'string', 'string']],
26+
'tcc_undefine_symbol': ['void', [TCCState_p, 'string']],
27+
'tcc_add_include_path': ['int', [TCCState_p, 'string']],
28+
'tcc_add_library': ['int', [TCCState_p, 'string']],
29+
'tcc_add_library_path': ['int', [TCCState_p, 'string']],
30+
'tcc_add_file': ['int', [TCCState_p, 'string']], // TODO: check unicode compat
31+
'tcc_compile_string': ['int', [TCCState_p, 'string']],
32+
'tcc_relocate': ['int', [TCCState_p, 'int']],
33+
'tcc_add_symbol': ['int', [TCCState_p, 'string', 'string']],
34+
'tcc_get_symbol': [void_p, [TCCState_p, 'string']],
35+
'tcc_run': ['int', [TCCState_p, 'int', void_p]]
36+
});
37+
this.ctx = this.lib.tcc_new();
38+
this.lib.tcc_set_lib_path(this.ctx, tccpath || './linux/lib/tcc');
39+
this.lib.tcc_set_output_type(this.ctx, 0); // memory build only
40+
this._obj_refs = {};
41+
}
42+
Tcc.prototype.set_option = function(option) {
43+
this.lib.tcc_set_option(this.ctx, option);
44+
}
45+
Tcc.prototype.define = function(symbol, value) {
46+
this.lib.tcc_define_symbol(this.ctx, symbol, value);
47+
}
48+
Tcc.prototype.undefine = function(symbol) {
49+
this.lib.tcc_undefine_symbol(this.ctx, symbol);
50+
}
51+
Tcc.prototype.add_include_path = function(path) {
52+
if (this.lib.tcc_add_include_path(this.ctx, path) == -1)
53+
throw new Error('error add_include_path: ' + path);
54+
}
55+
Tcc.prototype.add_library = function(library) {
56+
if (this.lib.tcc_add_library(this.ctx, library) == -1)
57+
throw new Error('error add_library: ' + library);
58+
}
59+
Tcc.prototype.add_link_path = function(path) {
60+
if (this.lib.tcc_add_library_path(this.ctx, path) == -1)
61+
throw new Error('error add_link_path: ' + path);
62+
}
63+
Tcc.prototype.add_file = function(path) {
64+
if (this.lib.tcc_add_file(this.ctx, path) == -1)
65+
throw new Error('error add_file: ' + path);
66+
}
67+
Tcc.prototype.compile = function(code) {
68+
if (this.lib.tcc_compile_string(this.ctx, code) == -1)
69+
throw new Error('error compile');
70+
}
71+
Tcc.prototype.relocate = function() {
72+
if (this.lib.tcc_relocate(this.ctx, 1) == -1)
73+
throw new Error('compile relocate');
74+
}
75+
Tcc.prototype.run = function(argc, argv) {
76+
// TODO: handle string array
77+
return this.lib.tcc_run(this.ctx, argc, argv);
78+
}
79+
Tcc.prototype.add_symbol = function(symbol, value) { // TODO: hold ref for value
80+
if (this.lib.tcc_add_symbol(this.ctx, symbol, value) == -1)
81+
throw new Error('error add_symbol: ' + symbol);
82+
}
83+
Tcc.prototype.get_symbol = function(symbol) {
84+
return this.lib.tcc_get_symbol(this.ctx, symbol);
85+
}
86+
Tcc.prototype.resolve_symbol = function(symbol, type) {
87+
// TODO: support array and struct
88+
if (typeof type === "function")
89+
return type(this.get_symbol(symbol));
90+
type = ref.coerceType(type);
91+
var res = this.get_symbol(symbol).reinterpret(type.size);
92+
res.type = type;
93+
return res;
94+
}
95+
Tcc.prototype.set_symbol = function(symbol, value) { // TODO: hold ref for value
96+
var buf = this.get_symbol(symbol).reinterpret(value.type.size);
97+
buf.type = value.type;
98+
ref.set(buf, 0, value.deref());
99+
}
100+
Tcc.prototype.set_function = function(symbol, cb) { // TODO: hold ref for value
101+
ref.set(this.resolve_symbol(symbol, 'void *'), 0, cb);
102+
}
103+
104+
105+
/**
106+
* C function type.
107+
* wrapper for lazy evaluation of ffi.ForeignFunction
108+
*/
109+
function CFuncType(restype, args) {
110+
return function(pointer) {
111+
return ffi.ForeignFunction(pointer, restype, args);
112+
}
113+
}
114+
115+
116+
/**
117+
* Create a C code object to be used with InlineGenerator.
118+
*/
119+
function Declaration(code, forward, symbols) {
120+
if (!(this instanceof(Declaration)))
121+
return new Declaration(code, forward, symbols);
122+
this.code = code || '';
123+
this.forward = forward || '';
124+
this.symbols = symbols || [];
125+
this.symbols_resolved = {};
126+
}
127+
128+
129+
/**
130+
* Code generator for inline C in Javascript. Handles back and forth symbol resolution.
131+
*/
132+
function InlineGenerator() {
133+
if (!(this instanceof InlineGenerator))
134+
return new InlineGenerator();
135+
var that = this;
136+
this.headerparts = [];
137+
this.parts = [];
138+
this.symbols = null;
139+
this.callables = [];
140+
}
141+
142+
/**
143+
* Get C code of added declarations.
144+
*/
145+
InlineGenerator.prototype.code = function() {
146+
var top = this.headerparts.join('\n');
147+
var forward = this.parts.map(function(el){return el.forward;}).join('\n');
148+
var code = this.parts.map(function(el){return el.code;}).join('\n');
149+
return [
150+
'/* top */', top, '',
151+
'/* forward */', forward, '',
152+
'/* code */', code, ''
153+
].join('\n');
154+
}
155+
156+
/**
157+
* Add a declaration to the generator.
158+
*/
159+
InlineGenerator.prototype.add_declaration = function(decl) {
160+
if (typeof decl === 'function')
161+
decl = decl.declaration;
162+
if (!(decl instanceof Declaration))
163+
throw new Error('cannot add declaration');
164+
this.parts.push(decl);
165+
}
166+
167+
/**
168+
* Add a topdeclaration to the generator.
169+
*/
170+
InlineGenerator.prototype.add_topdeclaration = function(decl) {
171+
this.headerparts.push(decl.code);
172+
}
173+
174+
/**
175+
* Bind a tcc state to this generator.
176+
* Resolves any pending symbols from declarations.
177+
* NOTE: the tcc state must be compiled and relocated. // TODO: test for relocate
178+
*/
179+
InlineGenerator.prototype.bind_state = function(state) {
180+
if (this.symbols)
181+
return this.symbols;
182+
var all_symbols = {};
183+
for (var i=0; i<this.parts.length; ++i) {
184+
if (this.parts[i].symbols.length)
185+
for (var j=0; j<this.parts[i].symbols.length; ++j) {
186+
var sym = this.parts[i].symbols[j];
187+
if (sym[0] instanceof FuncSymbol) {
188+
ref.set(state.resolve_symbol(sym[1], 'void *'), 0, sym[0].cb);
189+
} else {
190+
var resolved = state.resolve_symbol(sym[1], sym[0]);
191+
this.parts[i].symbols_resolved[sym[1]] = resolved;
192+
all_symbols[sym[1]] = resolved;
193+
}
194+
}
195+
}
196+
this.symbols = all_symbols;
197+
return all_symbols;
198+
}
199+
200+
/**
201+
* FuncSymbol - thin wrapper of ffi.Callback
202+
* needed to distingish type in `InlineGenerator.bind_state` for reverse symbol resolution of JS functions
203+
*/
204+
function FuncSymbol(restype, args, f) {
205+
if (!(this instanceof FuncSymbol))
206+
return new FuncSymbol(restype, args, f);
207+
this.cb = ffi.Callback(restype, args, f);
208+
}
209+
210+
/**
211+
* Convenvient function to import a function symbol from JS to C code.
212+
*/
213+
function c_callable(restype, name, args, f) {
214+
return new Declaration(
215+
'',
216+
restype + ' ' + '(*' + name + ')(' + args.join(', ') + ') = 0;',
217+
[[new FuncSymbol(restype, args, f), name]]
218+
);
219+
}
220+
221+
/**
222+
* Convenient function to create a C function in C useable from JS
223+
*/
224+
var c_function = function(restype, name, args, code) {
225+
var header = restype + ' ' + name + '(' + args.map(function(el){return el.join(' ')}).join(', ') + ')';
226+
var declaration = new Declaration(
227+
header + '\n{' + code + '}\n',
228+
header + ';',
229+
[[CFuncType(restype, args.map(function(el){return el[0];})), name]]
230+
);
231+
var func = function() {
232+
if (func.declaration.symbols_resolved[name])
233+
return func.declaration.symbols_resolved[name].apply(this, arguments);
234+
throw new Error('c_function "'+name+'" must be compiled and bound before usage');
235+
};
236+
func.declaration = declaration;
237+
return func;
238+
}
239+
240+
module.exports.Tcc = Tcc;
241+
module.exports.CFuncType = CFuncType;
242+
module.exports.InlineGenerator = InlineGenerator;
243+
module.exports.Declaration = Declaration;
244+
module.exports.c_function = c_function;
245+
module.exports.c_callable = c_callable;

install_tcc.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/sh
2+
echo $(pwd)
3+
rm -rf ./tinycc
4+
rm -rf ./build
5+
git clone git://repo.or.cz/tinycc.git
6+
mkdir build
7+
cd build
8+
../tinycc/configure --prefix=../linux --with-libgcc --disable-static
9+
make all
10+
make install

package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "node-tinycc",
3+
"version": "0.0.1",
4+
"description": "Inline C in Javascript with TinyCC.",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "node_modules/istanbul/lib/cli.js cover node_modules/mocha/bin/_mocha -- -R spec",
8+
"postinstall": "./install_tcc.sh"
9+
},
10+
"author": "",
11+
"license": "MIT",
12+
"dependencies": {
13+
"ffi": "^2.2.0",
14+
"ref": "^1.3.4",
15+
"ref-array": "^1.2.0",
16+
"ref-struct": "^1.1.0"
17+
},
18+
"devDependencies": {
19+
"istanbul": "^0.4.5",
20+
"mocha": "^3.4.2"
21+
}
22+
}

0 commit comments

Comments
 (0)