summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/Platform/Emscripten.cmake4
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js2
-rw-r--r--src/library_sdl.js2
-rw-r--r--tests/cmake/target_html/CMakeLists.txt5
-rw-r--r--tests/test_core.py60
-rw-r--r--tools/js-optimizer.js6
-rw-r--r--tools/validate_asmjs.py82
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())