diff options
author | max99x <max99x@gmail.com> | 2011-09-13 02:14:32 +0300 |
---|---|---|
committer | max99x <max99x@gmail.com> | 2011-09-13 02:14:32 +0300 |
commit | c5e5c90cd111c08b3d91d1bc89ba7d33a308cbc7 (patch) | |
tree | 5c6033729c4a4c5593fcd87bd9ffa6debf21f285 | |
parent | 91e29b31f280b879b10debf2194c2b87d94857cf (diff) | |
parent | 534cc7f05846daa88f2d3f2d149d9768262033e9 (diff) |
Merge remote-tracking branch 'upstream/master'
Conflicts:
src/intertyper.js
tests/runner.py
-rw-r--r-- | settings.py | 2 | ||||
-rw-r--r-- | src/analyzer.js | 36 | ||||
-rw-r--r-- | src/compiler.js | 3 | ||||
-rw-r--r-- | src/intertyper.js | 9 | ||||
-rw-r--r-- | src/jsifier.js | 9 | ||||
-rw-r--r-- | src/library.js | 12 | ||||
-rw-r--r-- | src/parseTools.js | 16 | ||||
-rw-r--r-- | src/preamble.js | 12 | ||||
-rw-r--r-- | src/settings.js | 10 | ||||
-rw-r--r-- | tests/runner.py | 123 | ||||
-rwxr-xr-x | tools/bindings_generator.py | 126 | ||||
-rwxr-xr-x | tools/emmaken.py | 23 | ||||
-rw-r--r-- | tools/shared.py | 3 |
13 files changed, 294 insertions, 90 deletions
diff --git a/settings.py b/settings.py index cf4fd28c..c6d63341 100644 --- a/settings.py +++ b/settings.py @@ -13,7 +13,7 @@ COMPILER_OPTS = ['-m32'] # Need to build as 32bit arch, for now - # various errors on 64bit compilation # WARNING: '-g' here will generate llvm bitcode that lli will crash on! -SPIDERMONKEY_ENGINE = [os.path.expanduser('~/Dev/mozilla-central/js/src/js'), '-m', '-j', '-p'] +SPIDERMONKEY_ENGINE = [os.path.expanduser('~/Dev/mozilla-central/js/src/js'), '-m', '-n'] V8_ENGINE = [os.path.expanduser('~/Dev/v8/d8')] #COMPILER_ENGINE=SPIDERMONKEY_ENGINE # XXX Warning: currently appears to be broken on trunk, some file reading issue diff --git a/src/analyzer.js b/src/analyzer.js index 96e6297b..824e7903 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -275,25 +275,27 @@ function analyzer(data) { } }); - // Second pass over variables - notice when types are crossed by bitcast - - func.lines.forEach(function(item) { - if (item.intertype === 'assign' && item.value.intertype === 'bitcast') { - // bitcasts are unique in that they convert one pointer to another. We - // sometimes need to know the original type of a pointer, so we save that. - // - // originalType is the type this variable is created from - // derivedTypes are the types that this variable is cast into - func.variables[item.ident].originalType = item.value.type2; - - if (!isNumber(item.value.ident)) { - if (!func.variables[item.value.ident].derivedTypes) { - func.variables[item.value.ident].derivedTypes = []; + if (QUANTUM_SIZE === 1) { + // Second pass over variables - notice when types are crossed by bitcast + + func.lines.forEach(function(item) { + if (item.intertype === 'assign' && item.value.intertype === 'bitcast') { + // bitcasts are unique in that they convert one pointer to another. We + // sometimes need to know the original type of a pointer, so we save that. + // + // originalType is the type this variable is created from + // derivedTypes are the types that this variable is cast into + func.variables[item.ident].originalType = item.value.type2; + + if (!isNumber(item.value.ident)) { + if (!func.variables[item.value.ident].derivedTypes) { + func.variables[item.value.ident].derivedTypes = []; + } + func.variables[item.value.ident].derivedTypes.push(item.value.type); } - func.variables[item.value.ident].derivedTypes.push(item.value.type); } - } - }); + }); + } for (vname in func.variables) { var variable = func.variables[vname]; diff --git a/src/compiler.js b/src/compiler.js index 8980da93..9bf81ba4 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -71,6 +71,9 @@ eval(processMacros(preprocess(read('runtime.js')))); // Read llvm var raw = read(ll_file); +if (FAKE_X86_FP80) { + raw = raw.replace(/x86_fp80/g, 'double'); +} var lines = raw.split('\n'); raw = null; diff --git a/src/intertyper.js b/src/intertyper.js index 7649203a..7d74dfd2 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -543,7 +543,9 @@ function intertyper(data, parseFunctions, baseLineNum) { item.intertype = 'bitcast'; item.type = item.tokens[4].text; // The final type Types.needAnalysis[item.type] = 0; - item.ident = toNiceIdent(item.tokens[2].text); + var to = getTokenIndexByText(item.tokens, 'to'); + item.params = [parseLLVMSegment(item.tokens.slice(2, to))]; + item.ident = item.params[0].ident; item.type2 = item.tokens[1].text; // The original type Types.needAnalysis[item.type2] = 0; this.forwardItem(item, 'Reintegrator'); @@ -791,13 +793,10 @@ function intertyper(data, parseFunctions, baseLineNum) { // external function stub substrate.addActor('External', { processItem: function(item) { - if (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR) { + if (item.tokens[1].text in LLVM.LINKAGES || item.tokens[1].text in LLVM.PARAM_ATTR || item.tokens[1].text in LLVM.VISIBILITIES) { item.tokens.splice(1, 1); } - if (item.tokens[1].text == 'hidden') { - item.tokens = [item.tokens[0]].concat(item.tokens.slice(2)); - } var params = parseParamTokens(item.tokens[3].item.tokens); return [{ intertype: 'functionStub', diff --git a/src/jsifier.js b/src/jsifier.js index 1124b008..1fd4a790 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -253,6 +253,10 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { constant = makePointer(constant, null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', item.type); var js = item.ident + '=' + constant + ';'; + // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations + if (item.ident.substr(0, 5) == '__ZTV') { + js += '\n' + makePointer('[0]', null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', ['void*']) + ';'; + } if (item.ident in EXPORTED_GLOBALS) { js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; } @@ -603,7 +607,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { makeFuncLineActor('store', function(item) { var value = finalizeLLVMParameter(item.value); if (pointingLevels(item.pointerType) == 1) { - value = parseNumerical(value, removePointing(item.pointerType)); + value = parseNumerical(value, item.valueType); } var impl = VAR_EMULATED; if (item.pointer.intertype == 'value') { @@ -776,7 +780,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { makeFuncLineActor('mathop', processMathop); makeFuncLineActor('bitcast', function(item) { - return item.ident; + return finalizeLLVMParameter(item.params[0]); }); function makeFunctionCall(ident, params, funcData) { @@ -876,6 +880,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { var preFile = BUILD_AS_SHARED_LIB ? 'preamble_sharedlib.js' : 'preamble.js'; var pre = processMacros(preprocess(read(preFile).replace('{{RUNTIME}}', getRuntime()), CONSTANTS)); print(pre); + print('Runtime.QUANTUM_SIZE = ' + QUANTUM_SIZE); if (RUNTIME_TYPE_INFO) { Types.cleanForRuntime(); print('Runtime.typeInfo = ' + JSON.stringify(Types.types)); diff --git a/src/library.js b/src/library.js index 88feb66c..156c1c25 100644 --- a/src/library.js +++ b/src/library.js @@ -3758,7 +3758,8 @@ LibraryManager.library = { strdup: function(ptr) { var len = String_len(ptr); var newStr = _malloc(len + 1); - {{{ makeCopyValues('newStr', 'ptr', 'len + 1', 'null', ' || 0') }}}; + {{{ makeCopyValues('newStr', 'ptr', 'len', 'null') }}}; + {{{ makeSetValue('newStr', 'len', '0', 'i8') }}}; return newStr; }, @@ -4120,6 +4121,14 @@ LibraryManager.library = { return -1; // 'indeterminable' for FLT_ROUNDS }, + llvm_memory_barrier: function(){}, + + llvm_atomic_load_add_i32_p0i32: function(ptr, delta) { + var ret = {{{ makeGetValue('ptr', '0', 'i32') }}}; + {{{ makeSetValue('ptr', '0', 'ret+delta', 'i32') }}}; + return ret; + }, + // ========================================================================== // iostream.h // ========================================================================== @@ -5285,6 +5294,7 @@ LibraryManager.library = { pthread_mutex_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, + pthread_cond_broadcast: function() {}, // ========================================================================== // malloc.h diff --git a/src/parseTools.js b/src/parseTools.js index 88758454..955353b2 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -74,7 +74,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 @@ -143,6 +143,7 @@ function isFunctionDef(token) { } function isFunctionType(type) { + type = type.replace(/"[^"]+"/g, '".."'); var parts = type.split(' '); if (pointingLevels(type) !== 1) return false; var text = removeAllPointing(parts.slice(1).join(' ')); @@ -430,6 +431,9 @@ function parseLLVMFunctionCall(segment) { if (type === '?') { if (intertype === 'getelementptr') { type = '*'; // a pointer, we can easily say, this is + } else if (intertype === 'bitcast') { + assert(segment[2].item.tokens.slice(-2)[0].text === 'to'); + type = segment[2].item.tokens.slice(-1)[0].text; } } var ret = { @@ -486,7 +490,15 @@ function IEEEUnHex(stringy) { stringy = stringy.substr(2); // leading '0x'; if (stringy.replace(/0/g, '') === '') return 0; while (stringy.length < 16) stringy = '0' + stringy; - assert(stringy.length === 16, 'Can only unhex 16-digit double numbers, nothing platform-specific'); + if (FAKE_X86_FP80 && stringy.length > 16) { + stringy = stringy.substr(stringy.length-16, 16); + if (!Debugging.shownIEEEUnHexWarning) { + dprint('WARNING: .ll contains floating-point values with more than 64 bits. Faking values for them.'); + dprint(' If they are used, this will almost certainly fail!'); + Debugging.shownIEEEUnHexWarning = true; + } + } + assert(stringy.length === 16, 'Can only unhex 16-digit double numbers, nothing platform-specific'); // |long double| can cause x86_fp80 which causes this var top = eval('0x' + stringy[0]); var neg = !!(top & 8); // sign if (neg) { diff --git a/src/preamble.js b/src/preamble.js index 9e0f5ce3..35bb75d7 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -320,7 +320,7 @@ function setValue(ptr, value, type) { default: abort('invalid type for setValue: ' + type); } } -this['setValue'] = setValue; +Module['setValue'] = setValue; // Parallel to setValue. @@ -338,7 +338,7 @@ function getValue(ptr, type) { } return null; } -this['getValue'] = getValue; +Module['getValue'] = getValue; // Allocates memory for some data and initializes it properly. @@ -428,7 +428,8 @@ var STACK_ROOT, STACKTOP, STACK_MAX; var STATICTOP; var HAS_TYPED_ARRAYS = false; -var TOTAL_MEMORY = 50*1024*1024; +var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}}; +var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; // Initialize the runtime's memory #if USE_TYPED_ARRAYS @@ -459,9 +460,8 @@ if (HAS_TYPED_ARRAYS) { } else #endif { - // Without this optimization, Chrome is slow. Sadly, the constant here needs to be tweaked depending on the code being run... - var FAST_MEMORY = TOTAL_MEMORY/32; - HEAP = new Array(FAST_MEMORY); + // Make sure that our HEAP is implemented as a flat array. + HEAP = new Array(TOTAL_MEMORY); for (var i = 0; i < FAST_MEMORY; i++) { HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed } diff --git a/src/settings.js b/src/settings.js index ed3437b1..ab532e67 100644 --- a/src/settings.js +++ b/src/settings.js @@ -34,6 +34,12 @@ INVOKE_RUN = 1; // Whether we will call run(). Disable if you embed the generate // code in your own, and will call run() yourself at the right time INIT_STACK = 1; // Whether to initialize memory on the stack to 0. INIT_HEAP = 0; // Whether to initialize memory anywhere other than the stack to 0. +FAST_MEMORY = 2*1024*1024; // The amount of memory to initialize to 0. This ensures it will be + // in a flat array. This only matters in non-typed array builds. +TOTAL_MEMORY = 50*1024*1024; // The total amount of memory to use. This mainly matters in + // typed array builds - accessing memory about this value will + // return undefined values and lead to serious problems, and there + // is currently no warning about that! // Code embetterments OPTIMIZE = 0; // Optimize llvm operations into js commands @@ -129,6 +135,10 @@ RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. // to more easily perform operations from handwritten JS on // objects with structures etc. +FAKE_X86_FP80 = 0; // Replaces x86_fp80 with double. This loses precision. It is better, + // if you can, to get the original source code to build without x86_fp80 + // (which is nonportable anyhow). + // Compiler debugging options DEBUG_TAGS_SHOWING = []; // Some useful items: diff --git a/tests/runner.py b/tests/runner.py index c52027a5..ee51238f 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -5,7 +5,7 @@ See settings.py file for options¶ms. Edit as needed. ''' from subprocess import Popen, PIPE, STDOUT -import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, json +import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, tempfile, re, json, difflib # Setup @@ -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', 'EXCEPTION_DEBUG']: + 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 @@ -223,17 +223,23 @@ class RunnerCore(unittest.TestCase): if type(value) is not str: value = value() # lazy loading if type(string) is not str: string = string() if value not in string: - raise Exception("Expected to find '%s' in '%s'" % (limit_size(value), limit_size(string))) + raise Exception("Expected to find '%s' in '%s', diff:\n\n%s" % ( + limit_size(value), limit_size(string), + limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(value.split('\n'), string.split('\n'), fromfile='expected', tofile='actual')])) + )) def assertNotContained(self, value, string): if type(value) is not str: value = value() # lazy loading if type(string) is not str: string = string() if value in string: - raise Exception("Expected to NOT find '%s' in '%s'" % (limit_size(value), limit_size(string))) + raise Exception("Expected to NOT find '%s' in '%s', diff:\n\n%s" % ( + limit_size(value), limit_size(string), + limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(value.split('\n'), string.split('\n'), fromfile='expected', tofile='actual')])) + )) ################################################################################################### -if 'benchmark' not in sys.argv: +if 'benchmark' not in str(sys.argv): # Tests print "Running Emscripten tests..." @@ -2987,7 +2993,6 @@ if 'benchmark' not in sys.argv: self.do_ll_test(path_from_root('tests', 'lua', 'lua.ll'), 'hello lua world!\n17\n1\n2\n3\n4\n7', - js_engines=[V8_ENGINE], # XXX Moz bug 675269 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')) @@ -2997,8 +3002,10 @@ if 'benchmark' not in sys.argv: # Build a library into a .bc file. We build the .bc file once and cache it for all our tests. (We cache in # memory since the test directory is destroyed and recreated for each test. Note that we cache separately # for different compilers) - def get_library(self, name, generated_libs, configure=['./configure'], configure_args=[], make=['make'], make_args=['-j', '2'], cache=True): + def get_library(self, name, generated_libs, configure=['./configure'], configure_args=[], make=['make'], make_args=['-j', '2'], cache=True, build_subdir=None): if type(generated_libs) is not list: generated_libs = [generated_libs] + if build_subdir and configure.startswith('./'): + configure = '.' + configure if GlobalCache is not None: cache_name = name + '|' + COMPILER @@ -3014,16 +3021,24 @@ if 'benchmark' not in sys.argv: project_dir = os.path.join(temp_dir, name) shutil.copytree(path_from_root('tests', name), project_dir) # Useful in debugging sometimes to comment this out os.chdir(project_dir) + if build_subdir: + try: + os.mkdir('cbuild') + except: + pass + os.chdir(os.path.join(project_dir, 'cbuild')) env = os.environ.copy() env['RANLIB'] = env['AR'] = env['CXX'] = env['CC'] = env['LIBTOOL'] = EMMAKEN env['EMMAKEN_COMPILER'] = COMPILER env['EMSCRIPTEN_TOOLS'] = path_from_root('tools') env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(COMPILER_OPTS + COMPILER_TEST_OPTS) # Normal CFLAGS is ignored by some configure's. - if configure: # Useful in debugging sometimes to comment this out (and 2 lines below) + if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |make| call) + env['EMMAKEN_JUST_CONFIGURE'] = '1' Popen(configure + configure_args, stdout=PIPE, stderr=STDOUT, env=env).communicate()[0] + del env['EMMAKEN_JUST_CONFIGURE'] Popen(make + make_args, stdout=PIPE, stderr=STDOUT, env=env).communicate()[0] bc_file = os.path.join(project_dir, 'bc.bc') - self.do_link(map(lambda lib: os.path.join(project_dir, lib), generated_libs), bc_file) + self.do_link(map(lambda lib: os.path.join(project_dir, 'cbuild', lib) if build_subdir else os.path.join(project_dir, lib), generated_libs), bc_file) if cache and GlobalCache is not None: print >> sys.stderr, '<save build into cache> ', GlobalCache[cache_name] = open(bc_file, 'rb').read() @@ -3032,7 +3047,7 @@ if 'benchmark' not in sys.argv: def get_freetype(self): global INIT_STACK; INIT_STACK = 1 # TODO: Investigate why this is necessary - return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.so')) + return self.get_library('freetype', os.path.join('objs', '.libs', 'libfreetype.a.bc')) def test_freetype(self): if QUANTUM_SIZE == 1: return self.skip() # TODO: Figure out and try to fix @@ -3066,10 +3081,25 @@ if 'benchmark' not in sys.argv: self.do_test(open(path_from_root('tests', 'zlib', 'example.c'), 'r').read(), open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(), - libraries=[self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a'])], + libraries=[self.get_library('zlib', os.path.join('libz.a.bc'), make_args=['libz.a'])], includes=[path_from_root('tests', 'zlib')], force_c=True) + def zzztest_glibc(self): + global CORRECT_SIGNS; CORRECT_SIGNS = 1 + global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 + global CORRECT_ROUNDINGS; CORRECT_ROUNDINGS = 1 + + self.do_test(r''' + #include <stdio.h> + int main() { printf("hai\n"); return 1; } + ''', + libraries=[self.get_library('glibc', [os.path.join('src', '.libs', 'libBulletCollision.a.bc'), + os.path.join('src', '.libs', 'libBulletDynamics.a.bc'), + os.path.join('src', '.libs', 'libLinearMath.a.bc')], + configure_args=['--disable-sanity-checks'])], + includes=[path_from_root('tests', 'glibc', 'include')]) + def test_the_bullet(self): # Called thus so it runs late in the alphabetical cycle... it is long global SAFE_HEAP, SAFE_HEAP_LINES, USE_TYPED_ARRAYS, LLVM_OPTS @@ -3087,9 +3117,9 @@ if 'benchmark' not in sys.argv: self.do_test(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), - libraries=[self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libLinearMath.a')], + libraries=[self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletCollision.a.bc'), + os.path.join('src', '.libs', 'libBulletDynamics.a.bc'), + os.path.join('src', '.libs', 'libLinearMath.a.bc')], configure_args=['--disable-demos','--disable-dependency-tracking'])], includes=[path_from_root('tests', 'bullet', 'src')], js_engines=[SPIDERMONKEY_ENGINE]) # V8 issue 1407 @@ -3152,10 +3182,10 @@ if 'benchmark' not in sys.argv: freetype = self.get_freetype() poppler = self.get_library('poppler', - [os.path.join('poppler', '.libs', 'libpoppler.so.13.0.0'), - os.path.join('goo', '.libs', 'libgoo.a'), - os.path.join('fofi', '.libs', 'libfofi.a'), - os.path.join('splash', '.libs', 'libsplash.a'), + [os.path.join('poppler', '.libs', 'libpoppler.so.13.0.0.bc'), + os.path.join('goo', '.libs', 'libgoo.a.bc'), + os.path.join('fofi', '.libs', 'libfofi.a.bc'), + os.path.join('splash', '.libs', 'libsplash.a.bc'), os.path.join('utils', 'pdftoppm.o'), os.path.join('utils', 'parseargs.o')], configure_args=['--disable-libjpeg', '--disable-libpng', '--disable-poppler-qt', '--disable-poppler-qt4', '--disable-cms']) @@ -3168,15 +3198,13 @@ if 'benchmark' not in sys.argv: self.do_ll_test(combined, lambda: map(ord, open(path_from_root('tests', 'poppler', 'ref.ppm'), 'r').read()).__str__().replace(' ', ''), args='-scale-to 512 paper.pdf filename'.split(' '), - post_build=post, - js_engines=[V8_ENGINE]) # XXX Moz bug 675269 + post_build=post) #, build_ll_hook=self.do_autodebug) def test_openjpeg(self): global USE_TYPED_ARRAYS global CORRECT_SIGNS if USE_TYPED_ARRAYS == 2: - return self.skip() # XXX Moz bug 675269 CORRECT_SIGNS = 1 else: CORRECT_SIGNS = 2 @@ -3201,7 +3229,7 @@ if 'benchmark' not in sys.argv: open(filename, 'w').write(src) lib = self.get_library('openjpeg', - [os.path.join('bin', 'libopenjpeg.so'), + [os.path.join('bin', 'libopenjpeg.so.1.4.0.bc'), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/index.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/convert.c.o'.split('/')), os.path.sep.join('codec/CMakeFiles/j2k_to_image.dir/__/common/color.c.o'.split('/')), @@ -3257,7 +3285,6 @@ if 'benchmark' not in sys.argv: os.path.join(self.get_building_dir(), 'openjpeg')], force_c=True, post_build=post, - js_engines=[V8_ENGINE], # XXX Moz bug 675269 output_nicerizer=image_compare)# build_ll_hook=self.do_autodebug) def test_python(self): @@ -3271,7 +3298,6 @@ if 'benchmark' not in sys.argv: self.do_ll_test(path_from_root('tests', 'python', 'python.ll'), 'hello python world!\n[0, 2, 4, 6]\n5\n22\n5.470000', - js_engines=[V8_ENGINE], # XXX Moz bug 675269 args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47'''], extra_emscripten_args=['-m']) @@ -3426,6 +3452,10 @@ if 'benchmark' not in sys.argv: Child2() : Parent(9) { printf("Child2:%d\\n", value); }; int getValCube() { return value*value*value; } static void printStatic() { printf("*static*\\n"); } + + virtual void virtualFunc() { printf("*virtualf*\\n"); } + virtual void virtualFunc2() { printf("*virtualf2*\\n"); } + static void runVirtualFunc(Child2 *self) { self->virtualFunc(); }; private: void doSomethingSecret() { printf("security breached!\\n"); }; // we should not be able to do this }; @@ -3501,6 +3531,32 @@ if 'benchmark' not in sys.argv: Child2.prototype.printStatic(); // static calls go through the prototype + // virtual function + c2.virtualFunc(); + Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + + // extend the class from JS + var c3 = new Child2; + customizeVTable(c3, [{ + original: Child2.prototype.virtualFunc, + replacement: function() { + print('*js virtualf replacement*'); + } + }, { + original: Child2.prototype.virtualFunc2, + replacement: function() { + print('*js virtualf2 replacement*'); + } + }]); + c3.virtualFunc(); + Child2.prototype.runVirtualFunc(c3); + c3.virtualFunc2(); + + c2.virtualFunc(); // original should remain the same + Child2.prototype.runVirtualFunc(c2); + c2.virtualFunc2(); + print('*ok*'); ''' @@ -3539,6 +3595,17 @@ Child2:9 0 1 *static* +*virtualf* +*virtualf* +*virtualf2* +Parent:9 +Child2:9 +*js virtualf replacement* +*js virtualf replacement* +*js virtualf2 replacement* +*virtualf* +*virtualf* +*virtualf2* *ok* ''', post_build=post2) @@ -3975,7 +4042,8 @@ TT = %s self.assertEquals(output, expected) else: - # Benchmarks + # Benchmarks. Run them with argument |benchmark|. To run a specific test, do + # |benchmark.test_X|. print "Running Emscripten benchmarks..." @@ -4008,6 +4076,7 @@ else: CORRECT_OVERFLOWS_LINES = CORRECT_SIGNS_LINES = CORRECT_ROUNDINGS_LINES = SAFE_HEAP_LINES = [] LLVM_OPTS = 1 DISABLE_EXCEPTIONS = 1 + FAST_MEMORY = 10*1024*1024 TEST_REPS = 4 TOTAL_TESTS = 6 @@ -4016,7 +4085,7 @@ else: total_times = map(lambda x: 0., range(TEST_REPS)) total_native_times = map(lambda x: 0., range(TEST_REPS)) - class Benchmark(RunnerCore): + class benchmark(RunnerCore): def print_stats(self, times, native_times): mean = sum(times)/len(times) squared_times = map(lambda x: x*x, times) @@ -4156,7 +4225,7 @@ else: old_quantum = QUANTUM_SIZE old_use_typed_arrays = USE_TYPED_ARRAYS QUANTUM_SIZE = 1 - USE_TYPED_ARRAYS = 0 # Rounding errors with TA2 are too big in this very rounding-sensitive code + USE_TYPED_ARRAYS = 0 # Rounding errors with TA2 are too big in this very rounding-sensitive code. However, TA2 is much faster (2X) src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read().replace('double', 'float') # benchmark with floats self.do_benchmark(src, ['7', '256'], '256 256') diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index deba8930..0a1a75cc 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -40,6 +40,9 @@ It's only purpose is to make it easy to access the C++ code in the JS bindings, and to prevent DFE from removing the code we care about. The JS bindings do more serious work, creating class structures in JS and linking them to the C bindings. + +NOTE: ammo.js is currently the biggest consumer of this code. For some + more docs you can see that project's README ''' import os, sys, glob, re @@ -105,6 +108,8 @@ for classname, clazz in parsed.classes.iteritems(): struct['name'] = sname # Missing in CppHeaderParser print 'zz seen struct %s in %s' % (sname, classname) +print 'zz parents: ', parents + for classname, clazz in classes.iteritems(): # Various precalculations print 'zz precalc', classname @@ -177,6 +182,9 @@ for classname, clazz in classes.iteritems(): 1/0. # Fill in some missing stuff + method['returns_text'] = method['returns_text'].replace('&', '').replace('*', '') + if method['returns_text'] in parents: + method['returns_text'] = parents[method['returns_text']] + '::' + method['returns_text'] if method.get('returns_const'): method['returns_text'] = 'const ' + method['returns_text'] if method.get('returns_pointer'): while method['returns_text'].count('*') < method['returns_pointer']: @@ -355,44 +363,119 @@ gen_js = open(basename + '.js', 'w') gen_c.write('extern "C" {\n') -# Use this when calling a binding function when you want to pass a null pointer. -# Having this object saves us needing to do checks for the object being null each time in the bindings code. gen_js.write(''' // Bindings utilities -var Object__cache = {}; -function wrapPointer(ptr, class_) { - var cache = class_ ? class_.prototype.__cache__ : Object__cache; + +var Object__cache = {}; // we do it this way so we do not modify |Object| +function wrapPointer(ptr, __class__) { + var cache = __class__ ? __class__.prototype.__cache__ : Object__cache; var ret = cache[ptr]; if (ret) return ret; - class_ = class_ || Object; - ret = Object.create(class_.prototype); + __class__ = __class__ || Object; + ret = Object.create(__class__.prototype); ret.ptr = ptr; + ret.__class__ = __class__; return cache[ptr] = ret; } -this['wrapPointer'] = wrapPointer; +Module['wrapPointer'] = wrapPointer; -function castObject(obj, class_) { - return wrapPointer(obj.ptr, class_); +function castObject(obj, __class__) { + return wrapPointer(obj.ptr, __class__); } -this['castObject'] = castObject; +Module['castObject'] = castObject; -this['NULL'] = wrapPointer(0); +Module['NULL'] = wrapPointer(0); function destroy(obj) { if (!obj['__destroy__']) throw 'Error: Cannot destroy object. (Did you create it yourself?)'; obj['__destroy__'](); + // Remove from cache, so the object can be GC'd and refs added onto it released + if (obj.__class__ !== Object) { + delete obj.__class__.prototype.__cache__[obj.ptr]; + } else { + delete Object__cache[obj.ptr]; + } } -this['destroy'] = destroy; +Module['destroy'] = destroy; function compare(obj1, obj2) { return obj1.ptr === obj2.ptr; } -this['compare'] = compare; +Module['compare'] = compare; function getPointer(obj) { return obj.ptr; } -this['getPointer'] = getPointer; +Module['getPointer'] = getPointer; + +function getClass(obj) { + return obj.__class__; +} +Module['getClass'] = getClass; + +function customizeVTable(object, replacementPairs) { + // Does not handle multiple inheritance + + // Find out vtable size + var vTable = getValue(object.ptr, 'void*'); + // This assumes our modification where we null-terminate vtables + var size = 0; + while (getValue(vTable + Runtime.QUANTUM_SIZE*size, 'void*')) { + size++; + } + + // Prepare replacement lookup table and add replacements to FUNCTION_TABLE + // There is actually no good way to do this! So we do the following hack: + // We create a fake vtable with canary functions, to detect which actual + // function is being called + var vTable2 = _malloc(size*Runtime.QUANTUM_SIZE); + setValue(object.ptr, vTable2, 'void*'); + var canaryValue; + var functions = FUNCTION_TABLE.length; + for (var i = 0; i < size; i++) { + var index = FUNCTION_TABLE.length; + (function(j) { + FUNCTION_TABLE.push(function() { + canaryValue = j; + }); + })(i); + FUNCTION_TABLE.push(0); + setValue(vTable2 + Runtime.QUANTUM_SIZE*i, index, 'void*'); + } + var args = [{ptr: 0}]; + replacementPairs.forEach(function(pair) { + // We need the wrapper function that converts arguments to not fail. Keep adding arguments til it works. + while(1) { + try { + pair['original'].apply(object, args); + break; + } catch(e) { + args.push(args[0]); + } + } + pair.originalIndex = getValue(vTable + canaryValue*Runtime.QUANTUM_SIZE, 'void*'); + }); + FUNCTION_TABLE = FUNCTION_TABLE.slice(0, functions); + + // Do the replacements + + var replacements = {}; + replacementPairs.forEach(function(pair) { + var replacementIndex = FUNCTION_TABLE.length; + FUNCTION_TABLE.push(pair['replacement']); + FUNCTION_TABLE.push(0); + replacements[pair.originalIndex] = replacementIndex; + }); + + // Copy and modify vtable + for (var i = 0; i < size; i++) { + var value = getValue(vTable + Runtime.QUANTUM_SIZE*i, 'void*'); + if (value in replacements) value = replacements[value]; + setValue(vTable2 + Runtime.QUANTUM_SIZE*i, value, 'void*'); + } + return object; +} +Module['customizeVTable'] = customizeVTable; ''') d |