diff options
author | Alon Zakai <alonzakai@gmail.com> | 2011-09-15 20:06:12 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2011-09-15 20:06:12 -0700 |
commit | 734b1450b5dc644065bd8705a5f1a7b8074cbc61 (patch) | |
tree | 2ddfabe609c907a3729e716407e8d1156bbff43c | |
parent | 534cc7f05846daa88f2d3f2d149d9768262033e9 (diff) | |
parent | 76c15819b22aca811e70127d82e146afa33036f4 (diff) |
Merge pull request #79 from max99x/master
Additions needed for emscripting Ruby
-rw-r--r-- | src/jsifier.js | 28 | ||||
-rw-r--r-- | src/library.js | 160 | ||||
-rw-r--r-- | src/parseTools.js | 2 | ||||
-rw-r--r-- | tests/exceptions/output.txt | 50 | ||||
-rw-r--r-- | tests/exceptions/typed.cpp | 91 | ||||
-rw-r--r-- | tests/runner.py | 9 |
6 files changed, 327 insertions, 13 deletions
diff --git a/src/jsifier.js b/src/jsifier.js index c19eda3a..9a2a82c4 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -207,6 +207,28 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { } var constant = null; if (item.external) { + // Import external global variables from the library if available. + var shortident = item.ident.slice(1); + if (LibraryManager.library[shortident] && + LibraryManager.library[shortident].length && + !BUILD_AS_SHARED_LIB) { + var val = LibraryManager.library[shortident]; + var padding; + if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { + padding = [item.type].concat(zeros(getNativeFieldSize(item.type))); + } else { + padding = makeEmptyStruct(item.type); + } + var padded = val.concat(padding.slice(val.length)); + var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, 'ALLOC_STATIC', item.type) + ';' + if (LibraryManager.library[shortident + '__postset']) { + js += '\n' + LibraryManager.library[shortident + '__postset']; + } + ret.push({ + intertype: 'GlobalVariablePostSet', + JS: js + }); + } return ret; } else { function needsPostSet(value) { @@ -714,6 +736,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var ret = '(function() { try { __THREW__ = false; return ' + call_ + ' ' + '} catch(e) { ' + + 'if (typeof e != "number") throw e; ' + 'if (ABORT) throw e; __THREW__ = true; ' + (EXCEPTION_DEBUG ? 'print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '') + 'return null } })(); if (!__THREW__) { ' + branch @@ -784,10 +807,11 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var argsTypes = []; var varargs = []; var varargsTypes = []; + var useJSArgs = (ident.slice(1) + '__jsargs') in LibraryManager.library; params.forEach(function(param, i) { var val = finalizeParam(param); - if (!func || !func.hasVarArgs || i < func.numParams-1) { + if (!func || !func.hasVarArgs || i < func.numParams-1 || useJSArgs) { args.push(val); argsTypes.push(param.type); } else { @@ -801,7 +825,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) }); varargs = varargs.map(function(vararg, i) { return vararg === 0 ? 0 : indexizeFunctions(vararg, varargsTypes[i]) }); - if (func && func.hasVarArgs) { + if (func && func.hasVarArgs && !useJSArgs) { if (varargs.length === 0) { varargs = [0]; varargsTypes = ['i32']; diff --git a/src/library.js b/src/library.js index 62e4de55..a628e323 100644 --- a/src/library.js +++ b/src/library.js @@ -255,7 +255,7 @@ LibraryManager.library = { xhr.open('GET', obj.url, false); // Some hints to the browser that we want binary data. - xhr.responseType = 'arraybuffer'; + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined'); } @@ -316,7 +316,7 @@ LibraryManager.library = { // Command line. result = readline(); } - if (!result) return null; + if (!result) result = ''; input.cache = intArrayFromString(result + '\n', true); } return input.cache.shift(); @@ -2103,6 +2103,7 @@ LibraryManager.library = { }, __01open64_: 'open', __01lseek64_: 'lseek', + __01truncate64_: 'truncate', __01ftruncate64_: 'ftruncate', // TODO: Check if any other aliases are needed. @@ -3078,6 +3079,7 @@ LibraryManager.library = { vfscanf: 'fscanf', vsscanf: 'sscanf', __01fopen64_: 'fopen', + __01freopen64_: 'freopen', __01fseeko64_: 'fseek', __01ftello64_: 'ftell', __01tmpfile64_: 'tmpfile', @@ -3584,6 +3586,11 @@ LibraryManager.library = { return String_len(ptr); }, + // TODO: Implement when we have real unicode support. + mblen: function() { + return 1; + }, + strspn: function(pstr, pset) { var str = String_copy(pstr, true); var set = String_copy(pset); @@ -3608,6 +3615,14 @@ LibraryManager.library = { } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); return pdest; }, + stpcpy: function(pdest, psrc) { + var i = 0; + do { + {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8') }}} + i ++; + } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); + return pdest + i - 1; + }, strncpy: function(pdest, psrc, num) { var padding = false, curr; @@ -3978,25 +3993,75 @@ LibraryManager.library = { return 1; }, - // Exceptions - minimal support, only (...) for now (no actual exception objects can be caught) + // Exceptions __cxa_allocate_exception: function(size) { - return _malloc(size); // warning: leaked + return _malloc(size); + }, + __cxa_free_exception: function(ptr) { + return _free(ptr); }, - __cxa_throw: function(ptr, data, dunno) { + __cxa_throw__deps: ['llvm_eh_exception'], + __cxa_throw: function(ptr, type, destructor) { #if EXCEPTION_DEBUG - print('Compiled code throwing an exception, ' + [ptr,data,dunno] + ', at ' + new Error().stack); + print('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); #endif + {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', '4', 'type', 'void*') }}} + {{{ makeSetValue('_llvm_eh_exception.buf', '8', 'destructor', 'void*') }}} throw ptr; }, + __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'], + __cxa_rethrow: function() { + ___cxa_end_catch.rethrown = true; + throw {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; + }, + llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);', llvm_eh_exception: function() { - return 'code-generated exception: ' + (new Error().stack); + return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; }, - llvm_eh_selector: function(exception, personality, num) { + llvm_eh_selector__jsargs: true, + llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { + var type = {{{ makeGetValue('_llvm_eh_exception.buf', '4', 'void*') }}} + for (var i = 2; i < arguments.length; i++) { + if (arguments[i] == type) return type; + } return 0; }, + llvm_eh_typeid_for: function(type) { + return type; + }, + _Unwind_Resume_or_Rethrow: function(ptr) { + throw ptr; + }, __cxa_begin_catch: function(ptr) { + return ptr; + }, + __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'], + __cxa_end_catch: function() { + if (___cxa_end_catch.rethrown) { + ___cxa_end_catch.rethrown = false; + return; + } + // Clear state flag. + __THREW__ = false; + // Clear type. + {{{ makeSetValue('_llvm_eh_exception.buf', '4', '0', 'void*') }}} + // Call destructor if one is registered then clear it. + var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; + var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', '8', 'void*') }}}; + if (destructor) { + FUNCTION_TABLE[destructor](ptr); + {{{ makeSetValue('_llvm_eh_exception.buf', '8', '0', 'i32') }}} + } + // Free ptr if it isn't null. + if (ptr) { + ___cxa_free_exception(ptr); + {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}} + } }, - __cxa_end_catch: function(ptr) { + __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], + __cxa_get_exception_ptr: function(ptr) { + return ptr; }, __cxa_call_unexpected: function(exception) { @@ -4004,9 +4069,29 @@ LibraryManager.library = { throw exception; }, + terminate: '__cxa_call_unexpected', + __gxx_personality_v0: function() { }, + // RTTI hacks for exception handling, defining type_infos for common types. + // The values are dummies. We simply use the addresses of these statically + // allocated variables as unique identifiers. + // type_info for int. + _ZTIi: [0], + // type_info for long. + _ZTIl: [0], + // type_info for long long. + _ZTIx: [0], + // type_info for float. + _ZTIf: [0], + // type_info for double. + _ZTId: [0], + // type_info for char. + _ZTIc: [0], + // type_info for void. + _ZTIv: [0], + llvm_umul_with_overflow_i32: function(x, y) { return { f0: x*y, @@ -4665,6 +4750,28 @@ LibraryManager.library = { // sys/time.h // ========================================================================== + __timespec_struct_layout: Runtime.generateStructInfo(null, '%struct.timespec'), + // TODO: Implement these for real. + clock_gettime__deps: ['__timespec_struct_layout'], + clock_gettime: function(clk_id, tp) { + // int clock_gettime(clockid_t clk_id, struct timespec *tp); + {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} + {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + return 0; + }, + clock_settime: function(clk_id, tp) { + // int clock_settime(clockid_t clk_id, const struct timespec *tp); + // Nothing. + return 0; + }, + clock_getres__deps: ['__timespec_struct_layout'], + clock_getres: function(clk_id, res) { + // int clock_getres(clockid_t clk_id, struct timespec *res); + {{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}} + {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} + return 0; + }, + // TODO: Implement remaining functions. // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html gettimeofday: function(ptr) { @@ -4735,6 +4842,23 @@ LibraryManager.library = { // TODO return 0; }, + sigemptyset: function(set) { + // int sigemptyset(sigset_t *set); + // TODO: Implement for real; don't hardcode offsets. + {{{ makeSetValue('set', '0', '0', 'i32') }}} + {{{ makeSetValue('set', '4', '0', 'i32') }}} + {{{ makeSetValue('set', '8', '0', 'i32') }}} + {{{ makeSetValue('set', '12', '0', 'i32') }}} + return 0; + }, + sigfillset: 'sigemptyset', + sigdelset: 'sigemptyset', + sigaction: function(set) { + // int sigemptyset(sigset_t *set); + // TODO: Implement for real. + return 0; + }, + sigprocmask: 'sigaction', __libc_current_sigrtmin: function() { return 0; }, @@ -5145,6 +5269,24 @@ LibraryManager.library = { __errno_location: function() { return ___setErrNo.ret; }, + // ========================================================================== + // sys/resource.h + // ========================================================================== + + // TODO: Implement for real. + __rlimit_struct_layout: Runtime.generateStructInfo(null, '%struct.rlimit'), + getrlimit__deps: ['__rlimit_struct_layout'], + getrlimit: function(resource, rlp) { + // int getrlimit(int resource, struct rlimit *rlp); + {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_cur', '-1', 'i32') }}} // RLIM_INFINITY + {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_max', '-1', 'i32') }}} // RLIM_INFINITY + return 0; + }, + setrlimit: function(resource, rlp) { + // int setrlimit(int resource, const struct rlimit *rlp) + return 0; + }, + __01getrlimit64_: 'getrlimit', // ========================================================================== // pthread.h (stubs for mutexes only - no thread support yet!) diff --git a/src/parseTools.js b/src/parseTools.js index a3347139..955353b2 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -1050,7 +1050,7 @@ function getGetElementPtrIndexes(item) { } curr = 0; } - type = typeData ? typeData.fields[curr] : ''; + type = typeData && typeData.fields[curr] ? typeData.fields[curr] : ''; }); var ret = indexes[0]; for (var i = 1; i < indexes.length; i++) { diff --git a/tests/exceptions/output.txt b/tests/exceptions/output.txt new file mode 100644 index 00000000..2db8345e --- /dev/null +++ b/tests/exceptions/output.txt @@ -0,0 +1,50 @@ +*CREATING A FOO +*CREATING A BAR +*CREATING A QUUX +start + + + throwing ExFooInstance +*COPYING A FOO +*COPYING A FOO +outer catch foo: 11 +*DESTROYING A FOO (11) +*DESTROYING A FOO (11) + + + throwing ExBarInstance +*COPYING A BAR +*COPYING A BAR +inner re-throw: 22 +*DESTROYING A BAR (22) +outer catch bar-ref: 22 +*DESTROYING A BAR (22) + + + throwing ExQuuxInstance +*COPYING A QUUX +*COPYING A QUUX +inner catch quux: 33 +*DESTROYING A QUUX (33) +*DESTROYING A QUUX (33) + + + + + + + throwing 42 +outer catch int: 42 + + + throwing NULL +outer catch-all + + + not throwing + + +end +*DESTROYING A QUUX (33) +*DESTROYING A BAR (22) +*DESTROYING A FOO (11) diff --git a/tests/exceptions/typed.cpp b/tests/exceptions/typed.cpp new file mode 100644 index 00000000..29bf548a --- /dev/null +++ b/tests/exceptions/typed.cpp @@ -0,0 +1,91 @@ +#include <stdio.h> + +class ExFoo { +public: + int x; + ExFoo(int x) { this->x = x; printf("*CREATING A FOO\n"); } + ExFoo(const ExFoo& other) { x=other.x; printf("*COPYING A FOO\n"); } + ~ExFoo() { printf("*DESTROYING A FOO (%d)\n", x); } +} ExFooInstance(11); +class ExBar { +public: + int x; + ExBar(int x) { this->x = x; printf("*CREATING A BAR\n"); } + ExBar(const ExBar& other) { x=other.x; printf("*COPYING A BAR\n"); } + ~ExBar() { printf("*DESTROYING A BAR (%d)\n", x); } +} ExBarInstance(22); +class ExQuux { +public: + int x; + ExQuux(int x) { this->x = x; printf("*CREATING A QUUX\n"); } + ExQuux(const ExQuux& other) { x=other.x; printf("*COPYING A QUUX\n"); } + ~ExQuux() { printf("*DESTROYING A QUUX (%d)\n", x); } +} ExQuuxInstance(33); +// NOTE: Throwing pointers and polymorphic matching not supported. +// class ExChild : public ExQuux { +// public: +// ExChild(int x) : ExQuux(x) { printf("*CREATING A CHILD\n"); } +// ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); } +// ~ExChild() { printf("*DESTROYING A CHILD (%d)\n", x); } +// } ExChildInstance(44); + +void magic(int which) { + try { + switch (which) { + case 0: + printf(" throwing ExFooInstance\n"); + throw ExFooInstance; + case 1: + printf(" throwing ExBarInstance\n"); + throw ExBarInstance; + case 2: + printf(" throwing ExQuuxInstance\n"); + throw ExQuuxInstance; +// NOTE: Throwing pointers and polymorphic matching not supported. +// case 3: +// printf(" throwing ExQuux ptr\n"); +// throw &ExQuuxInstance; +// case 4: +// printf(" throwing ExChildInstance\n"); +// throw ExChildInstance; + case 5: + printf(" throwing 42\n"); + throw 42; + case 6: + printf(" throwing NULL\n"); + throw (void*)0; + case 7: + printf(" not throwing\n"); + } + } catch (ExQuux e1) { + printf("inner catch quux: %d\n", e1.x); + } catch (ExBar e2) { + printf("inner re-throw: %d\n", e2.x); + throw; + } +} + +int main() { + printf("start\n\n\n"); + for (int i = 0; i < 8; i++) { + try { + magic(i); + } catch (ExFoo e1) { + printf("outer catch foo: %d\n", e1.x); + } catch (ExBar& e2) { + printf("outer catch bar-ref: %d\n", e2.x); +// NOTE: Throwing pointers and polymorphic matching not supported. +// } catch (ExQuux& e3) { +// printf("outer catch quux-ref: %d\n", e3.x); +// } catch (ExQuux* e4) { +// printf("outer catch quux-ptr: %d\n", e4->x); + } catch (int e5) { + printf("outer catch int: %d\n", e5); + } catch (...) { + printf("outer catch-all\n"); + } + printf("\n\n"); + } + printf("end\n"); + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index 25b3eda5..ee51238f 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -178,7 +178,7 @@ class RunnerCore(unittest.TestCase): def do_emscripten(self, filename, output_processor=None, append_ext=True, extra_args=[]): # Run Emscripten exported_settings = {} - for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTIONS', 'FAST_MEMORY']: + for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTIONS', 'FAST_MEMORY', 'EXCEPTION_DEBUG']: try: value = eval(setting) exported_settings[setting] = value @@ -972,6 +972,13 @@ if 'benchmark' not in str(sys.argv): DISABLE_EXCEPTIONS = 1 self.do_test(src, 'Compiled code throwing an exception') + def test_typed_exceptions(self): + global SAFE_HEAP; SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. + global EXCEPTION_DEBUG; EXCEPTION_DEBUG = 0 # Messes up expected output. + src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() + expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() + self.do_test(src, expected) + def test_class(self): src = ''' #include <stdio.h> |