diff options
-rw-r--r-- | cmake/Platform/Emscripten.cmake | 4 | ||||
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 2 | ||||
-rw-r--r-- | src/library_sdl.js | 2 | ||||
-rw-r--r-- | tests/cmake/target_html/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/test_core.py | 60 | ||||
-rw-r--r-- | tools/js-optimizer.js | 6 | ||||
-rw-r--r-- | tools/validate_asmjs.py | 82 |
8 files changed, 138 insertions, 25 deletions
diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake index 9bfa829d..ec3f5383 100644 --- a/cmake/Platform/Emscripten.cmake +++ b/cmake/Platform/Emscripten.cmake @@ -135,3 +135,7 @@ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "-O2" CACHE STRING "Emscripten-over set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_RELEASE") set(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL") set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO "-O2" CACHE STRING "Emscripten-overridden CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO") + +function(em_validate_asmjs_after_build target) + add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo Validating build output for asm.js... COMMAND "python" ARGS "${EMSCRIPTEN_ROOT_PATH}/tools/validate_asmjs.py" "$<TARGET_FILE:${target}>") +endfunction() diff --git a/src/intertyper.js b/src/intertyper.js index f9633549..c4f62ec4 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -709,7 +709,7 @@ function intertyper(data, sidePass, baseLineNums) { if (item.ident == 'asm') { if (ASM_JS) { Types.hasInlineJS = true; - warnOnce('inline JavaScript (asm, EM_ASM) will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance'); + warnOnce('inline JavaScript (asm, EM_ASM) will cause the code to no longer fall in the asm.js subset of JavaScript, which can reduce performance - consider using emscripten_run_script'); } assert(TARGET_LE32, 'inline js is only supported in le32'); // Inline assembly is just JavaScript that we paste into the code diff --git a/src/jsifier.js b/src/jsifier.js index a3b26aa9..49f2c564 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -1833,7 +1833,7 @@ function JSify(data, functionsOnly, givenFunctions) { } if (CORRUPTION_CHECK) { - assert(!ASM_JS); // cannot monkeypatch asm! + assert(!ASM_JS, 'corruption checker is not compatible with asm.js'); print(processMacros(read('corruptionCheck.js'))); } if (HEADLESS) { diff --git a/src/library_sdl.js b/src/library_sdl.js index 91c3c4bd..9383834f 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -755,7 +755,7 @@ var LibrarySDL = { document.addEventListener("keydown", SDL.receiveEvent); document.addEventListener("keyup", SDL.receiveEvent); document.addEventListener("keypress", SDL.receiveEvent); - document.addEventListener("blur", SDL.receiveEvent); + window.addEventListener("blur", SDL.receiveEvent); document.addEventListener("visibilitychange", SDL.receiveEvent); } window.addEventListener("unload", SDL.receiveEvent); diff --git a/tests/cmake/target_html/CMakeLists.txt b/tests/cmake/target_html/CMakeLists.txt index 8b0528eb..b5c69417 100644 --- a/tests/cmake/target_html/CMakeLists.txt +++ b/tests/cmake/target_html/CMakeLists.txt @@ -14,3 +14,8 @@ SET(CMAKE_EXECUTABLE_SUFFIX ".html") add_executable(hello_world_gles ${sourceFiles}) set_target_properties(hello_world_gles PROPERTIES LINK_FLAGS "${linkFlags}") + +# Validating asm.js requires SpiderMonkey JS VM - detect its presence via the SPIDERMONKEY environment variable. +if (DEFINED ENV{SPIDERMONKEY} AND CMAKE_BUILD_TYPE STREQUAL Release) + em_validate_asmjs_after_build(hello_world_gles) +endif() diff --git a/tests/test_core.py b/tests/test_core.py index 054721f9..06ef76b2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7562,6 +7562,7 @@ def process(filename): def test_utf32(self): if self.emcc_args is None: return self.skip('need libc for wcslen()') + if not self.is_le32(): return self.skip('this test uses inline js, which requires le32') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.') self.do_run(open(path_from_root('tests', 'utf32.cpp')).read(), 'OK.', args=['-fshort-wchar']) @@ -8647,29 +8648,44 @@ def process(filename): Settings.SAFE_HEAP_LINES = ['btVoronoiSimplexSolver.h:40', 'btVoronoiSimplexSolver.h:41', 'btVoronoiSimplexSolver.h:42', 'btVoronoiSimplexSolver.h:43'] - def test(): - self.do_run(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), - [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings - open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), - open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], - libraries=self.get_library('bullet', [os.path.join('src', '.libs', 'libBulletDynamics.a'), - os.path.join('src', '.libs', 'libBulletCollision.a'), - os.path.join('src', '.libs', 'libLinearMath.a')], - configure_args=['--disable-demos','--disable-dependency-tracking']), - includes=[path_from_root('tests', 'bullet', 'src')]) - test() - - assert 'asm2g' in test_modes - if self.run_name == 'asm2g': - # Test forced alignment - print >> sys.stderr, 'testing FORCE_ALIGNED_MEMORY' - old = open('src.cpp.o.js').read() - Settings.FORCE_ALIGNED_MEMORY = 1 + configure_commands = [['sh', './configure'], ['cmake', '.']] + configure_args = [['--disable-demos','--disable-dependency-tracking'], ['-DBUILD_DEMOS=OFF', '-DBUILD_EXTRAS=OFF']] + for c in range(0,2): + configure = configure_commands[c] + # Windows cannot run configure sh scripts. + if WINDOWS and configure[0] == 'sh': + continue + + # Depending on whether 'configure' or 'cmake' is used to build, Bullet places output files in different directory structures. + if configure[0] == 'sh': + generated_libs = [os.path.join('src', '.libs', 'libBulletDynamics.a'), + os.path.join('src', '.libs', 'libBulletCollision.a'), + os.path.join('src', '.libs', 'libLinearMath.a')] + else: + generated_libs = [os.path.join('src', 'BulletDynamics', 'libBulletDynamics.a'), + os.path.join('src', 'BulletCollision', 'libBulletCollision.a'), + os.path.join('src', 'LinearMath', 'libLinearMath.a')] + + def test(): + self.do_run(open(path_from_root('tests', 'bullet', 'Demos', 'HelloWorld', 'HelloWorld.cpp'), 'r').read(), + [open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read(), # different roundings + open(path_from_root('tests', 'bullet', 'output2.txt'), 'r').read(), + open(path_from_root('tests', 'bullet', 'output3.txt'), 'r').read()], + libraries=self.get_library('bullet', generated_libs, configure=configure, configure_args=configure_args[c], cache_name_extra=configure[0]), + includes=[path_from_root('tests', 'bullet', 'src')]) test() - new = open('src.cpp.o.js').read() - print len(old), len(new), old.count('tempBigInt'), new.count('tempBigInt') - assert len(old) > len(new) - assert old.count('tempBigInt') > new.count('tempBigInt') + + assert 'asm2g' in test_modes + if self.run_name == 'asm2g' and configure[0] == 'sh': + # Test forced alignment + print >> sys.stderr, 'testing FORCE_ALIGNED_MEMORY' + old = open('src.cpp.o.js').read() + Settings.FORCE_ALIGNED_MEMORY = 1 + test() + new = open('src.cpp.o.js').read() + print len(old), len(new), old.count('tempBigInt'), new.count('tempBigInt') + assert len(old) > len(new) + assert old.count('tempBigInt') > new.count('tempBigInt') def test_poppler(self): if self.emcc_args is None: return self.skip('very slow, we only do this in emcc runs') diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index c029ab4e..e78f39ef 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1746,6 +1746,7 @@ function registerize(ast) { // We also mark local variables - i.e., having a var definition var localVars = {}; var hasSwitch = false; // we cannot optimize variables if there is a switch, unless in asm mode + var hasFunction = false; traverse(fun, function(node, type) { if (type === 'var') { node[1].forEach(function(defined) { localVars[defined[0]] = 1 }); @@ -1757,8 +1758,13 @@ function registerize(ast) { } } else if (type === 'switch') { hasSwitch = true; + } else if (type === 'function') { + hasFunction = true; } }); + if (!asm && hasFunction) { + return; // inline assembly, and not asm (where we protect it in normalize/denormalize), so abort registerize pass + } vacuum(fun); if (extraInfo && extraInfo.globals) { assert(asm); diff --git a/tools/validate_asmjs.py b/tools/validate_asmjs.py new file mode 100644 index 00000000..ea909fbd --- /dev/null +++ b/tools/validate_asmjs.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +# This is a helper script to validate a file for asm.js. + +# cmdline usage: 'python validate_asmjs.py filename.{html/js}' +# Prints a line starting with 'OK: ' on success, and returns process exit code 0. +# On failure, prints a line starting with 'FAIL: ', and returns a nonzero process exit code. + +# python usage: 'validate_asmjs("filename.{html/js}", muteOutput=True/False)' +# Returns True/False depending on whether the file was valid asm.js. + +# This script depends on the SpiderMonkey JS engine, which must be present in PATH in order for this script to function. + +import subprocess, sys, re, tempfile, os, time +import shared + +# Looks up SpiderMonkey engine using the variable SPIDERMONKEY_ENGINE in ~/.emscripten, and if not set up there, via PATH. +def find_spidermonkey_engine(): + sm_engine = shared.SPIDERMONKEY_ENGINE if hasattr(shared, 'SPIDERMONKEY_ENGINE') else [''] + if not sm_engine or len(sm_engine[0]) == 0 or not os.path.exists(sm_engine[0]): + sm_engine[0] = shared.Building.which('js') + if sm_engine[0] == None: + return ['js-not-found'] + return sm_engine + +# Given a .js file, returns True/False depending on if that file is valid asm.js +def validate_asmjs_jsfile(filename, muteOutput): + process = subprocess.Popen(find_spidermonkey_engine() + ['-c', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + (stdout, stderr) = process.communicate() + if not muteOutput: + if len(stdout.strip()) > 0: + print stdout.strip() + if len(stderr.strip()) > 0: + # Pretty-print the output not to contain a spurious warning. + stderr = stderr.replace('warning: successfully compiled asm.js', ' successfully compiled asm.js') + + print >> sys.stderr, stderr.strip() + if 'successfully compiled asm.js' in stderr: + return True + else: + return False + +# This tool takes as input a file built with Emscripten (either .html or .js) and validates it for asm.js. +# Returns True/False denoting whether the file was valid asm.js. In case of a .html file, all <script>content</script> tags are searched, +# and the ones containing a "use asm" section are validated. +def validate_asmjs(filename, muteOutput): + if filename.endswith('.html'): + html = open(filename, 'r').read() + matches = re.findall('''<\w*script\w*.*?>(.*?)<\w*/script\w*>''', html, re.DOTALL | re.MULTILINE) + numAsmJsBlocks = 0 + for match in matches: + if '"use asm"' in match: + numAsmJsBlocks = numAsmJsBlocks + 1 + tmp_js = tempfile.mkstemp(suffix='.js') + os.write(tmp_js[0], match) + os.close(tmp_js[0]) + valid_asmjs = validate_asmjs_jsfile(tmp_js[1], muteOutput) + os.remove(tmp_js[1]) + if not valid_asmjs: + return False + if numAsmJsBlocks == 0: + if not muteOutput: + print >> sys.stderr, 'Error: the file does not contain any "use asm" modules.' + return False + else: + return True + else: + return validate_asmjs_jsfile(filename, muteOutput) + +def main(): + if len(sys.argv) < 2: + print 'Usage: validate_asmjs <filename>' + return 2 + if validate_asmjs(sys.argv[1], muteOutput=False): + print "OK: File '" + sys.argv[1] + "' validates as asm.js" + return 0 + else: + print "FAIL: File '" + sys.argv[1] + "' is not valid asm.js" + return 1 + +if __name__ == '__main__': + sys.exit(main()) |