diff options
-rw-r--r-- | src/jsifier.js | 77 | ||||
-rw-r--r-- | src/library.js | 164 | ||||
-rw-r--r-- | src/parseTools.js | 2 | ||||
-rw-r--r-- | src/preamble.js | 2 | ||||
-rw-r--r-- | tests/files.cpp | 33 | ||||
-rw-r--r-- | tests/runner.py | 37 |
6 files changed, 260 insertions, 55 deletions
diff --git a/src/jsifier.js b/src/jsifier.js index a88429a0..847b198d 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -278,56 +278,83 @@ function JSify(data, functionsOnly, givenTypes, givenFunctions, givenGlobalVaria return ret; } else { item.JS = 'var ' + item.ident + ';'; - var constant = item.external ? - makePointer(JSON.stringify(makeEmptyStruct(item.type)) + ' /* external value? */', null, 'ALLOC_STATIC') - : - parseConst(item.value, item.type, item.ident); - if (typeof constant === 'object') { - // This is a flattened object. We need to find its idents, so they can be assigned to later - constant.forEach(function(value, i) { - if (value[0] in set('_', '(') || value.substr(0, 14) === 'CHECK_OVERFLOW') { // ident, or expression containing an ident - ret.push({ - intertype: 'GlobalVariablePostSet', - JS: 'IHEAP[' + item.ident + '+' + i + '] = ' + value + ';', - }); - constant[i] = '0'; - } + var constant = null; + if (item.external) { + return ret; + } else { + constant = parseConst(item.value, item.type, item.ident); + if (typeof constant === 'object') { + // This is a flattened object. We need to find its idents, so they can be assigned to later + constant.forEach(function(value, i) { + if (value[0] in set('_', '(') || value.substr(0, 14) === 'CHECK_OVERFLOW') { // ident, or expression containing an ident + ret.push({ + intertype: 'GlobalVariablePostSet', + JS: 'IHEAP[' + item.ident + '+' + i + '] = ' + value + ';' + }); + constant[i] = '0'; + } + }); + constant = '[' + constant.join(', ') + ']'; + } + constant = makePointer(constant, null, 'ALLOC_STATIC', item.type); + + return ret.concat({ + intertype: 'GlobalVariable', + JS: item.ident + ' = ' + constant + ';', }); - constant = '[' + constant.join(', ') + ']'; } - constant = makePointer(constant, null, 'ALLOC_STATIC', item.type); - return ret.concat({ - intertype: 'GlobalVariable', - JS: item.ident + ' = ' + constant + ';', - }); } } }); + var addedLibraryItems = {}; + // functionStub substrate.addActor('FunctionStub', { processItem: function(item) { + var ret = [item]; var shortident = item.ident.substr(1); if (shortident in Library) { function addFromLibrary(ident) { var me = arguments.callee; - if (!me.added) me.added = {}; - if (ident in me.added) return ''; - me.added[ident] = true; + if (ident in addedLibraryItems) return ''; + addedLibraryItems[ident] = true; var snippet = Library[ident]; if (typeof snippet === 'string') { if (Library[snippet]) { snippet = Library[snippet]; // redirection for aliases } + } else if (typeof snippet === 'object') { + // JSON.stringify removes functions, so we need to make sure they are added + var funcs = []; + for (var x in snippet) { + if (typeof snippet[x] === 'function') { + funcs.push(x + ': ' + snippet[x].toString()); + } + } + snippet = JSON.stringify(snippet).replace(/}$/, ', ' + funcs.join(', ') + ' }'); + } else if (typeof snippet === 'function') { + snippet = snippet.toString(); + } + + var postsetId = ident + '__postset'; + var postset = Library[postsetId]; + if (postset && !addedLibraryItems[postsetId]) { + addedLibraryItems[postsetId] = true; + ret.push({ + intertype: 'GlobalVariablePostSet', + JS: postset + }); } + var deps = Library[ident + '__deps']; - return '_' + ident + ' = ' + snippet.toString() + (deps ? '\n' + deps.map(addFromLibrary).join('\n') : ''); + return '_' + ident + ' = ' + snippet + (deps ? '\n' + deps.map(addFromLibrary).join('\n') : ''); } item.JS = addFromLibrary(shortident); } else { item.JS = '// stub for ' + item.ident; } - return [item]; + return ret; } }); diff --git a/src/library.js b/src/library.js index 495229c3..8bc35ac0 100644 --- a/src/library.js +++ b/src/library.js @@ -6,7 +6,8 @@ // entry in the Library is a function, we insert it. If it is a string, we // do another lookup in the library (a simple way to write a function once, // if it can be called by different names). We also allow dependencies, -// using __deps. +// using __deps. Initialization code to be run after allocating all +// global constants can be defined by __postset. // // Note that the full function name will be '_' + the name in the Library // object. For convenience, the short name appears here. Note that if you add a @@ -15,6 +16,7 @@ var Library = { // stdio.h + _formatString__deps: ['STDIO'], _formatString: function() { function isFloatArg(type) { return String.fromCharCode(type) in Runtime.set('f', 'e', 'g'); @@ -142,6 +144,14 @@ var Library = { _strcpy(str, __formatString.apply(null, args)); // not terribly efficient }, + snprintf__deps: ['strncpy', '_formatString'], + snprintf: function() { + var str = arguments[0]; + var num = arguments[1]; + var args = Array.prototype.slice.call(arguments, 2); + _strncpy(str, __formatString.apply(null, args), num); // not terribly efficient + }, + fflush: function(file) { __print__(null); }, @@ -163,11 +173,6 @@ var Library = { }, _ZNSo3putEc: 'putchar', - fopen: function(filename, mode) { - return 1; // TODO - }, - __01fopen64_: 'fopen', - getc: function(file) { return -1; // EOF }, @@ -178,23 +183,6 @@ var Library = { return chr; }, - feof: function(stream) { - return 1; - }, - - ferror: function(stream) { - return 0; - }, - - fwrite: function(ptr, size, count, stream) { - __print__(intArrayToString(Array_copy(ptr, count))); - return count; - }, - - fclose: function(stream) { - return 0; - }, - _ZNSo5flushEv: function() { __print__('\n'); }, @@ -226,6 +214,115 @@ var Library = { funlockfile: function(file) { }, + // stdio.h - file functions + + STDIO__postset: 'this._STDIO.init()', + STDIO: { + streams: {}, + filenames: {}, + counter: 1, + SEEK_SET: 0, /* Beginning of file. */ + SEEK_CUR: 1, /* Current position. */ + SEEK_END: 2, /* End of file. */ + init: function() { + _stdin = Pointer_make([0], null, ALLOC_STATIC); + IHEAP[_stdin] = this.prepare('<<stdin>>'); + _stdout = Pointer_make([0], null, ALLOC_STATIC); + IHEAP[_stdout] = this.prepare('<<stdout>>'); + _stderr = Pointer_make([0], null, ALLOC_STATIC); + IHEAP[_stderr] = this.prepare('<<stderr>>'); + }, + prepare: function(filename, data) { + var stream = this.counter++; + this.streams[stream] = { + filename: filename, + data: data, + position: 0, + eof: 0, + error: 0, + print: 1 // true for stdout and stderr - we print when receiving data for them + }; + this.filenames[filename] = stream; + return stream; + } + }, + + fopen__deps: ['STDIO'], + fopen: function(filename, mode) { + var str = Pointer_stringify(filename); + //assert(str in this._STDIO.filenames, 'No information for file: ' + str); + return this._STDIO.filenames[str]; + }, + __01fopen64_: 'fopen', + + rewind__deps: ['STDIO'], + rewind: function(stream) { + var info = this._STDIO.streams[stream]; + info.position = 0; + info.error = 0; + }, + + fseek__deps: ['STDIO'], + fseek: function(stream, offset, whence) { + var info = this._STDIO.streams[stream]; + if (whence === this._STDIO.SEEK_CUR) { + offset += info.position; + } else if (whence === this._STDIO.SEEK_END) { + offset += info.data.length; + } + info.position = offset; + info.eof = 0; + return 0; + }, + __01fseeko64_: 'fseek', + + ftell__deps: ['STDIO'], + ftell: function(stream) { + return this._STDIO.streams[stream].position; + }, + __01ftello64_: 'ftell', + + fread__deps: ['STDIO'], + fread: function(ptr, size, count, stream) { + var info = this._STDIO.streams[stream]; + for (var i = 0; i < count; i++) { + if (info.position + size > info.data.length) { + info.eof = 1; + return i; + } + for (var j = 0; j < size; j++) { + {{{ makeSetValue('ptr', '0', 'info.data[info.position]', 'null') }}} + info.position++; + ptr++; + } + } + return count; + }, + + fwrite__deps: ['STDIO'], + fwrite: function(ptr, size, count, stream) { + var info = this._STDIO.streams[stream]; + if (info.print) { + __print__(intArrayToString(Array_copy(ptr, count*size))); + } // XXX + return count; + }, + + fclose__deps: ['STDIO'], + fclose: function(stream) { + return 0; + }, + + feof__deps: ['STDIO'], + feof: function(stream) { + return this._STDIO.streams[stream].eof; + }, + + ferror__deps: ['STDIO'], + ferror: function(stream) { + return this._STDIO.streams[stream].error; + }, + // stdlib.h abs: 'Math.abs', @@ -796,6 +893,14 @@ var Library = { return -1; }, + getuid: function() { + return 100; + }, + + getpwuid: function(uid) { + return 0; // NULL + }, + // time.h time: function(ptr) { @@ -882,6 +987,19 @@ var Library = { me.ret = Pointer_make([0], 0, ALLOC_STATIC); } return me.ret; + }, + + // pthread.h (stubs for mutexes only - no thread support yet!) + + pthread_mutex_init: function() {}, + pthread_mutex_destroy: function() {}, + pthread_mutex_lock: function() {}, + pthread_mutex_unlock: function() {}, + + // dirent.h + + opendir: function(pname) { + return 0; } }; diff --git a/src/parseTools.js b/src/parseTools.js index 12b531ea..52afcfe7 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -57,7 +57,7 @@ function toNiceIdent(ident) { assert(ident); if (parseFloat(ident) == ident) return ident; if (ident == 'null') return '0'; // see parseNumerical - return ident.replace('%', '$').replace(/["\\ \.@:<>,\*\[\]-]/g, '_'); + return ident.replace('%', '$').replace(/["&\\ \.@:<>,\*\[\]-]/g, '_'); } // Kind of a hack. In some cases we have strings that we do not want diff --git a/src/preamble.js b/src/preamble.js index ad7ea12a..e375b90a 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -268,8 +268,6 @@ function __shutdownRuntime__() { } } -// stdio.h - // Copies a list of num items on the HEAP into a // a normal JavaScript array of numbers diff --git a/tests/files.cpp b/tests/files.cpp new file mode 100644 index 00000000..a07ef42b --- /dev/null +++ b/tests/files.cpp @@ -0,0 +1,33 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +int main() { + FILE *file = fopen("somefile.binary", "rb"); + assert(file); + + fseek(file, 0, SEEK_END); + int size = ftell(file); + rewind (file); + printf("size: %d\n", size); + + char *buffer = (char*) malloc (sizeof(char)*size); + assert(buffer); + + size_t read = fread(buffer, 1, size, file); + assert(read == size); + + printf("data: %d", buffer[0]); + for (int i = 1; i < size; i++) + printf(",%d", buffer[i]); + printf("\n"); + + fclose (file); + free (buffer); + + fwrite("texto\n", 1, 6, stdout); + fwrite("texte\n", 1, 6, stderr); + + return 0; +} + diff --git a/tests/runner.py b/tests/runner.py index 86647fbb..817a681a 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -91,7 +91,11 @@ class RunnerCore(unittest.TestCase): if LLVM_OPTS: shutil.move(filename + '.o', filename + '.o.pre') output = Popen([LLVM_OPT, filename + '.o.pre'] + LLVM_OPT_OPTS + ['-o=' + filename + '.o'], stdout=PIPE, stderr=STDOUT).communicate()[0] - + + def do_llvm_dis(self, filename): + # LLVM binary ==> LLVM assembly + Popen([LLVM_DIS, filename + '.o'] + LLVM_DIS_OPTS + ['-o=' + filename + '.o.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0] + # Build JavaScript code from source code def build(self, src, dirname, filename, output_processor=None, main_file=None): # Copy over necessary files for compiling the source @@ -125,8 +129,7 @@ class RunnerCore(unittest.TestCase): self.do_llvm_opts(filename) - # LLVM binary ==> LLVM assembly - output = Popen([LLVM_DIS, filename + '.o'] + LLVM_DIS_OPTS + ['-o=' + filename + '.o.ll'], stdout=PIPE, stderr=STDOUT).communicate()[0] + self.do_llvm_dis(filename) self.do_emscripten(filename, output_processor) @@ -187,11 +190,19 @@ if 'benchmark' not in sys.argv: #shutil.rmtree(dirname) # TODO: leave no trace in memory. But for now nice for debugging - # No building - just process an existing .ll file + # No building - just process an existing .ll file (or .bc, which we turn into .ll) def do_ll_test(self, ll_file, output, args=[], js_engines=None, output_nicerizer=None): if COMPILER != LLVM_GCC: return # We use existing .ll, so which compiler is unimportant filename = os.path.join(self.get_dir(), 'src.cpp') + + if ll_file.endswith('.bc'): + shutil.copy(ll_file, filename + '.o') + self.do_llvm_dis(filename) + shutil.copy(filename + '.o.ll', filename + '.o.ll.in') + os.remove(filename + '.o.ll') + ll_file = filename + '.o.ll.in' + if LLVM_OPTS: shutil.copy(ll_file, filename + '.o.ll.pre') Popen([LLVM_AS, filename + '.o.ll.pre'] + ['-o=' + filename + '.o'], stdout=PIPE, stderr=STDOUT).communicate()[0] @@ -1283,6 +1294,16 @@ if 'benchmark' not in sys.argv: # Bloated memory; same layout as C/C++ self.do_test(src, '*16,0,4,8,8,12|20,0,4,4,8,12,12,16|24,0,20,0,4,4,8,12,12,16*\n*0,0,0,1,2,64,68,69,72*\n*2*') + def test_files(self): + def post(filename): + src = open(filename, 'r').read().replace( + '// {{PRE_RUN_ADDITIONS}}', + '''this._STDIO.prepare('somefile.binary', [100, 200, 50, 25, 10, 77, 123]);''' + ) + open(filename, 'w').write(src) + src = open(path_from_root('tests', 'files.cpp'), 'r').read() + self.do_test(src, 'size: 7\ndata: 100,200,50,25,10,77,123\ntexto\ntexte\n', post_build=post) + ### 'Big' tests def test_fannkuch(self): @@ -1361,6 +1382,14 @@ if 'benchmark' not in sys.argv: args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n')) + def zzztest_poppler(self): + # Has 'Object', which has a big union with a value that can be of any type (like a dynamic value) + global SAFE_HEAP; SAFE_HEAP = 0 + + self.do_ll_test(path_from_root('tests', 'poppler', 'pdftoppm.bc'), + 'halp', + args='-png -l 1 -scale-to 512 ~/Dev/emscripten/docs/paper.pdf filename'.split(' ')) + def test_python(self): # Overflows in string_hash global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 |