aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/Platform/Emscripten.cmake110
-rw-r--r--cmake/Platform/Emscripten_unix.cmake24
-rwxr-xr-xemcc29
-rwxr-xr-xemscripten.py16
-rw-r--r--src/intertyper.js4
-rw-r--r--src/jsifier.js9
-rw-r--r--src/library_browser.js10
-rw-r--r--src/library_sdl.js39
-rw-r--r--src/modules.js7
-rw-r--r--src/postamble.js14
-rw-r--r--src/preamble.js72
-rw-r--r--src/proxyClient.js74
-rw-r--r--src/proxyWorker.js143
-rw-r--r--src/runtime.js6
-rw-r--r--src/settings.js3
-rwxr-xr-xsystem/bin/sdl-config13
-rw-r--r--tests/cmake/target_html/CMakeLists.txt13
-rw-r--r--tests/cmake/target_js/CMakeLists.txt24
-rw-r--r--tests/msvc10/glbook_15_Hello_Triangle_KD.vcxproj108
-rw-r--r--tests/msvc10/tests_msvc10.sln197
-rwxr-xr-xtests/runner.py6
-rw-r--r--tests/sdl_canvas_proxy.c34
-rw-r--r--tests/sdl_canvas_proxy.pngbin0 -> 4488 bytes
-rw-r--r--tests/sdl_key_proxy.c63
-rw-r--r--tests/test_browser.py68
-rw-r--r--tests/test_core.py102
-rw-r--r--tests/test_other.py119
-rw-r--r--tests/utf32.cpp53
-rw-r--r--tools/js-optimizer.js18
-rw-r--r--tools/shared.py112
-rw-r--r--tools/test-js-optimizer-asm-outline1-output.js133
-rw-r--r--tools/test-js-optimizer-asm-outline1.js40
-rw-r--r--tools/test-js-optimizer-regs-output.js16
-rw-r--r--tools/test-js-optimizer-regs.js2
-rw-r--r--tools/validate_asmjs.py82
35 files changed, 1391 insertions, 372 deletions
diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake
index aafd38a8..ec3f5383 100644
--- a/cmake/Platform/Emscripten.cmake
+++ b/cmake/Platform/Emscripten.cmake
@@ -1,48 +1,124 @@
# This file is a 'toolchain description file' for CMake.
-# It teaches CMake about the Emscripten compiler, so that CMake can generate Unix Makefiles
+# It teaches CMake about the Emscripten compiler, so that CMake can generate makefiles
# from CMakeLists.txt that invoke emcc.
# To use this toolchain file with CMake, invoke CMake with the following command line parameters
-# cmake -DEMSCRIPTEN=1
-# -DCMAKE_TOOLCHAIN_FILE=<EmscriptenRoot>/cmake/Platform/Emscripten.cmake
-# -DCMAKE_MODULE_PATH=<EmscriptenRoot>/cmake
+# cmake -DCMAKE_TOOLCHAIN_FILE=<EmscriptenRoot>/cmake/Platform/Emscripten.cmake
# -DCMAKE_BUILD_TYPE=<Debug|RelWithDebInfo|Release|MinSizeRel>
-# -G "Unix Makefiles"
+# -G "Unix Makefiles" (Linux and OSX)
+# -G "MinGW Makefiles" (Windows)
# <path/to/CMakeLists.txt> # Note, pass in here ONLY the path to the file, not the filename 'CMakeLists.txt' itself.
# After that, build the generated Makefile with the command 'make'. On Windows, you may download and use 'mingw32-make' instead.
-# the name of the target operating system
-set(CMAKE_SYSTEM_NAME Emscripten)
+# The following variable describes the target OS we are building to.
+# Ideally, this could be 'Emscripten', but as Emscripten mimics the Linux platform, setting this to Linux will allow more of existing software to build.
+# Be sure to run Emscripten test_openjpeg if planning to change this.
+set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
+set(CMAKE_CROSSCOMPILING TRUE)
+
+# Do a no-op access on the CMAKE_TOOLCHAIN_FILE variable so that CMake will not issue a warning on it being unused.
+if (CMAKE_TOOLCHAIN_FILE)
+endif()
+
+# Locate where the Emscripten compiler resides in relative to this toolchain file.
+if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "")
+ get_filename_component(GUESS_EMSCRIPTEN_ROOT_PATH "${CMAKE_CURRENT_LIST_DIR}/../../" ABSOLUTE)
+ if (EXISTS "${GUESS_EMSCRIPTEN_ROOT_PATH}/emranlib")
+ set(EMSCRIPTEN_ROOT_PATH "${GUESS_EMSCRIPTEN_ROOT_PATH}")
+ endif()
+endif()
+
+# If not found by above search, locate using the EMSCRIPTEN environment variable.
+if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "")
+ set(EMSCRIPTEN_ROOT_PATH "$ENV{EMSCRIPTEN}")
+endif()
+
+# Abort if not found.
if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "")
- set(CMAKE_FIND_ROOT_PATH "$ENV{EMSCRIPTEN}")
+ message(FATAL_ERROR "Could not locate the Emscripten compiler toolchain directory! Either set the EMSCRIPTEN environment variable, or pass -DEMSCRIPTEN_ROOT_PATH=xxx to CMake to explicitly specify the location of the compiler!")
+endif()
+
+# Normalize, convert Windows backslashes to forward slashes or CMake will crash.
+get_filename_component(EMSCRIPTEN_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}" ABSOLUTE)
+
+if ("${CMAKE_MODULE_PATH}" STREQUAL "")
+ set(CMAKE_MODULE_PATH "${EMSCRIPTEN_ROOT_PATH}/cmake")
+endif()
+
+set(CMAKE_FIND_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}/cmake")
+
+if (CMAKE_HOST_WIN32)
+ set(EMCC_SUFFIX ".bat")
else()
- set(CMAKE_FIND_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}")
+ set(EMCC_SUFFIX "")
endif()
# Specify the compilers to use for C and C++
if ("${CMAKE_C_COMPILER}" STREQUAL "")
- set(CMAKE_C_COMPILER "emcc.bat")
- set(CMAKE_CXX_COMPILER "em++.bat")
- set(CMAKE_AR "emar.bat")
- set(CMAKE_RANLIB "emranlib.bat")
+ set(CMAKE_C_COMPILER "${EMSCRIPTEN_ROOT_PATH}/emcc${EMCC_SUFFIX}")
+endif()
+if ("${CMAKE_CXX_COMPILER}" STREQUAL "")
+ set(CMAKE_CXX_COMPILER "${EMSCRIPTEN_ROOT_PATH}/em++${EMCC_SUFFIX}")
+endif()
+
+if ("${CMAKE_AR}" STREQUAL "")
+ set(CMAKE_AR "${EMSCRIPTEN_ROOT_PATH}/emar${EMCC_SUFFIX}")
endif()
+if ("${CMAKE_RANLIB}" STREQUAL "")
+ set(CMAKE_RANLIB "${EMSCRIPTEN_ROOT_PATH}/emranlib${EMCC_SUFFIX}")
+endif()
+
+# Don't do compiler autodetection, since we are cross-compiling.
+include(CMakeForceCompiler)
+CMAKE_FORCE_C_COMPILER("${CMAKE_C_COMPILER}" Clang)
+CMAKE_FORCE_CXX_COMPILER("${CMAKE_CXX_COMPILER}" Clang)
+
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+# We would prefer to specify a standard set of Clang+Emscripten-friendly common convention for suffix files, especially for CMake executable files,
+# but if these are adjusted, ${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake will fail, since it depends on being able to compile output files with predefined names.
+#SET(CMAKE_LINK_LIBRARY_SUFFIX "")
+#SET(CMAKE_STATIC_LIBRARY_PREFIX "")
+#SET(CMAKE_STATIC_LIBRARY_SUFFIX ".bc")
+#SET(CMAKE_SHARED_LIBRARY_PREFIX "")
+#SET(CMAKE_SHARED_LIBRARY_SUFFIX ".bc")
+#IF (NOT CMAKE_EXECUTABLE_SUFFIX)
+# SET(CMAKE_EXECUTABLE_SUFFIX ".js")
+#endif()
+#SET(CMAKE_FIND_LIBRARY_PREFIXES "")
+#SET(CMAKE_FIND_LIBRARY_SUFFIXES ".bc")
+
+SET(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
+SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
+SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
+SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
+
+set(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@")
+set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@")
+
# Specify the program to use when building static libraries. Force Emscripten-related command line options to clang.
-set(CMAKE_CXX_ARCHIVE_CREATE "${CMAKE_CXX_COMPILER} -o <TARGET> -emit-llvm <LINK_FLAGS> <OBJECTS>")
-set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER} -o <TARGET> -emit-llvm <LINK_FLAGS> <OBJECTS>")
+set(CMAKE_CXX_ARCHIVE_CREATE "${CMAKE_CXX_COMPILER} ${CMAKE_START_TEMP_FILE} -o <TARGET> -emit-llvm <LINK_FLAGS> <OBJECTS>${CMAKE_END_TEMP_FILE}")
+set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER} ${CMAKE_START_TEMP_FILE} -o <TARGET> -emit-llvm <LINK_FLAGS> <OBJECTS>${CMAKE_END_TEMP_FILE}")
# Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to detect when building using Emscripten.
# There seems to be some kind of bug with CMake, so you might need to define this manually on the command line with "-DEMSCRIPTEN=1".
set(EMSCRIPTEN 1)
+# We are cross-compiling, so unset the common CMake variables that represent the target platform. Leave UNIX define enabled, since Emscripten
+# mimics a Linux environment.
+SET(WIN32)
+SET(APPLE)
+
+set(CMAKE_C_SIZEOF_DATA_PTR 4)
+set(CMAKE_CXX_SIZEOF_DATA_PTR 4)
+
set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_RELEASE")
set(CMAKE_C_FLAGS_MINSIZEREL "-DNDEBUG" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_MINSIZEREL")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "" CACHE STRING "Emscripten-overridden CMAKE_C_FLAGS_RELWITHDEBINFO")
@@ -59,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/cmake/Platform/Emscripten_unix.cmake b/cmake/Platform/Emscripten_unix.cmake
deleted file mode 100644
index 92a21fd1..00000000
--- a/cmake/Platform/Emscripten_unix.cmake
+++ /dev/null
@@ -1,24 +0,0 @@
-# On Unix platforms, we must specify the absolute path to emcc for cmake, having emcc in PATH will cause cmake to fail finding it.
-# The user must set the EMSCRIPTEN variable to point to the Emscripten root folder.
-
-# Try locating Emscripten root directory based on the location of this toolchain file.
-get_filename_component(GUESS_EMSCRIPTEN_ROOT_PATH "${CMAKE_CURRENT_LIST_FILE}/../../.." ABSOLUTE)
-if (EXISTS "${GUESS_EMSCRIPTEN_ROOT_PATH}/emcc")
- set(EMSCRIPTEN_ROOT_PATH "${GUESS_EMSCRIPTEN_ROOT_PATH}")
-endif()
-
-# If not found, try if the environment variable Emscripten was set.
-if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "")
- if ("$ENV{EMSCRIPTEN}" STREQUAL "")
- message(ERROR "Could not locate emcc and the environment variable EMSCRIPTEN has not been set! Please point it to Emscripten root directory!")
- else()
- set(EMSCRIPTEN_ROOT_PATH "$ENV{EMSCRIPTEN}")
- endif()
-endif()
-
-set(CMAKE_C_COMPILER "${EMSCRIPTEN_ROOT_PATH}/emcc")
-set(CMAKE_CXX_COMPILER "${EMSCRIPTEN_ROOT_PATH}/em++")
-set(CMAKE_AR "${EMSCRIPTEN_ROOT_PATH}/emar")
-set(CMAKE_RANLIB "${EMSCRIPTEN_ROOT_PATH}/emranlib")
-
-include(${EMSCRIPTEN_ROOT_PATH}/cmake/Platform/Emscripten.cmake)
diff --git a/emcc b/emcc
index 958ed7cd..4a49cc12 100755
--- a/emcc
+++ b/emcc
@@ -471,6 +471,9 @@ Options that are modified or new in %s include:
to hide these warnings and acknowledge that the
explicit use of absolute paths is intentional.
+ --proxy-to-worker Generates both html and js files. The main
+ program is in js, and the html proxies to/from it.
+
The target file, if specified (-o <target>), defines what will
be generated:
@@ -740,6 +743,7 @@ try:
save_bc = False
memory_init_file = False
use_preload_cache = False
+ proxy_to_worker = False
if use_cxx:
default_cxx_std = '-std=c++03' # Enforce a consistent C++ standard when compiling .cpp files, if user does not specify one on the cmdline.
@@ -903,6 +907,9 @@ try:
memory_init_file = int(newargs[i+1])
newargs[i] = ''
newargs[i+1] = ''
+ elif newargs[i] == '--proxy-to-worker':
+ proxy_to_worker = True
+ newargs[i] = ''
elif newargs[i].startswith(('-I', '-L')):
path_name = newargs[i][2:]
if not absolute_warning_shown and os.path.isabs(path_name):
@@ -1128,6 +1135,9 @@ try:
if shared.Settings.ASM_JS and shared.Settings.DLOPEN_SUPPORT:
assert shared.Settings.DISABLE_EXCEPTION_CATCHING, 'no exceptions support with dlopen in asm yet'
+ if proxy_to_worker:
+ shared.Settings.PROXY_TO_WORKER = 1
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
@@ -1144,7 +1154,7 @@ try:
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
if input_file.endswith(CXX_SUFFIXES):
args += shared.EMSDK_CXX_OPTS
- logging.debug("running:" + call + ' ' + ' '.join(args))
+ logging.debug("running: " + call + ' ' + ' '.join(args))
execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that)
if not os.path.exists(output_file):
logging.error('compiler frontend failed to generate LLVM bitcode, halting')
@@ -1709,7 +1719,11 @@ try:
logging.debug('generating HTML')
shell = open(shell_path).read()
html = open(target, 'w')
- if not Compression.on:
+ if proxy_to_worker:
+ html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename)))
+ js_target = unsuffixed(target) + '.js'
+ shutil.copyfile(final, js_target)
+ elif not Compression.on:
if debug_level >= 4:
match = re.match('.*?<script[^>]*>{{{ SCRIPT_CODE }}}</script>', shell,
re.DOTALL)
@@ -1783,13 +1797,12 @@ try:
html.close()
else:
if split_js_file:
- from tools.split import split_javascript_file
- split_javascript_file(final, unsuffixed(target), split_js_file)
+ from tools.split import split_javascript_file
+ split_javascript_file(final, unsuffixed(target), split_js_file)
else:
- if debug_level >= 4: generate_source_map(target)
-
- # copy final JS to output
- shutil.move(final, target)
+ if debug_level >= 4: generate_source_map(target)
+ # copy final JS to output
+ shutil.move(final, target)
if DEBUG: logging.debug('total time: %.2f seconds' % (time.time() - start_time))
diff --git a/emscripten.py b/emscripten.py
index 257527fe..4d744fdd 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -43,6 +43,12 @@ NUM_CHUNKS_PER_CORE = 1.25
MIN_CHUNK_SIZE = 1024*1024
MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes
+STDERR_FILE = os.environ.get('EMCC_STDERR_FILE')
+if STDERR_FILE:
+ STDERR_FILE = os.path.abspath(STDERR_FILE)
+ print >> sys.stderr, 'logging stderr in js compiler phase into %s' % STDERR_FILE
+ STDERR_FILE = open(STDERR_FILE, 'w')
+
def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)):
try:
funcs_file = temp_files.get('.func_%d.ll' % i).name
@@ -57,6 +63,7 @@ def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libr
engine=compiler_engine,
args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries,
stdout=subprocess.PIPE,
+ stderr=STDERR_FILE,
cwd=path_from_root('src'))
except KeyboardInterrupt:
# Python 2.7 seems to lock up when a child process throws KeyboardInterrupt
@@ -179,7 +186,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
if out and DEBUG: print >> sys.stderr, ' loading pre from jcache'
if not out:
open(pre_file, 'w').write(pre_input)
- out = jsrun.run_js(compiler, compiler_engine, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE,
+ out = jsrun.run_js(compiler, compiler_engine, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE,
cwd=path_from_root('src'))
assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?'
if jcache:
@@ -287,6 +294,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
exported_implemented_functions = set()
for func_js, curr_forwarded_data in outputs:
curr_forwarded_json = json.loads(curr_forwarded_data)
+ forwarded_json['Types']['hasInlineJS'] = forwarded_json['Types']['hasInlineJS'] or curr_forwarded_json['Types']['hasInlineJS']
forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed']
for key, value in curr_forwarded_json['Functions']['blockAddresses'].iteritems():
forwarded_json['Functions']['blockAddresses'][key] = value
@@ -376,7 +384,7 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
if DEBUG: t = time.time()
post_file = temp_files.get('.post.ll').name
open(post_file, 'w').write('\n') # no input, just processing of forwarded data
- out = jsrun.run_js(compiler, compiler_engine, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE,
+ out = jsrun.run_js(compiler, compiler_engine, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, stderr=STDERR_FILE,
cwd=path_from_root('src'))
post, last_forwarded_data = out.split('//FORWARDED_DATA:') # if this fails, perhaps the process failed prior to printing forwarded data?
last_forwarded_json = json.loads(last_forwarded_data)
@@ -543,7 +551,7 @@ function asmPrintFloat(x, y) {
}
// EMSCRIPTEN_START_ASM
var asm = (function(global, env, buffer) {
- 'use asm';
+ %s
var HEAP8 = new global.Int8Array(buffer);
var HEAP16 = new global.Int16Array(buffer);
var HEAP32 = new global.Int32Array(buffer);
@@ -552,7 +560,7 @@ var asm = (function(global, env, buffer) {
var HEAPU32 = new global.Uint32Array(buffer);
var HEAPF32 = new global.Float32Array(buffer);
var HEAPF64 = new global.Float64Array(buffer);
-''' % (asm_setup,) + '\n' + asm_global_vars + '''
+''' % (asm_setup, "'use asm';" if not forwarded_json['Types']['hasInlineJS'] and not settings['SIDE_MODULE'] else "'almost asm';") + '\n' + asm_global_vars + '''
var __THREW__ = 0;
var threwValue = 0;
var setjmpId = 0;
diff --git a/src/intertyper.js b/src/intertyper.js
index ddb93d71..082fd993 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -708,13 +708,15 @@ function intertyper(data, sidePass, baseLineNums) {
item.ident = eatLLVMIdent(tokensLeft);
if (item.ident == 'asm') {
if (ASM_JS) {
- warnOnce('inline JS in asm.js mode can cause the code to no longer fall in the asm.js subset of JavaScript');
+ 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 - 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
item.intertype = 'value';
if (tokensLeft[0].text == 'sideeffect') tokensLeft.splice(0, 1);
item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly
+ assert((item.tokens[5].text.match(/=/g) || []).length <= 1, 'we only support at most 1 exported variable from inline js: ' + item.ident);
var i = 0;
var params = [], args = [];
splitTokenList(tokensLeft[3].item.tokens).map(function(element) {
diff --git a/src/jsifier.js b/src/jsifier.js
index 38f3bd5e..49f2c564 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -324,11 +324,13 @@ function JSify(data, functionsOnly, givenFunctions) {
assert(typeof constant === 'object');//, [typeof constant, JSON.stringify(constant), item.external]);
// This is a flattened object. We need to find its idents, so they can be assigned to later
+ var structTypes = null;
constant.forEach(function(value, i) {
if (needsPostSet(value)) { // ident, or expression containing an ident
+ if (!structTypes) structTypes = generateStructTypes(item.type);
ret.push({
intertype: 'GlobalVariablePostSet',
- JS: makeSetValue(makeGlobalUse(item.ident), i, value, 'i32', false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors
+ JS: makeSetValue(makeGlobalUse(item.ident), i, value, structTypes[i], false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors
});
constant[i] = '0';
}
@@ -1831,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) {
@@ -1841,6 +1843,9 @@ function JSify(data, functionsOnly, givenFunctions) {
print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace("'?%s'", "'/'").replace('%s,', 'null,').replace('%d', '0'));
print('}');
}
+ if (PROXY_TO_WORKER) {
+ print(read('proxyWorker.js'));
+ }
if (RUNTIME_TYPE_INFO) {
Types.cleanForRuntime();
print('Runtime.typeInfo = ' + JSON.stringify(Types.types));
diff --git a/src/library_browser.js b/src/library_browser.js
index 235ccc78..e4966e15 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -724,7 +724,15 @@ mergeInto(LibraryManager.library, {
Module['preMainLoop']();
}
- Runtime.dynCall('v', func);
+ try {
+ Runtime.dynCall('v', func);
+ } catch (e) {
+ if (e instanceof ExitStatus) {
+ return;
+ } else {
+ throw e;
+ }
+ }
if (Module['postMainLoop']) {
Module['postMainLoop']();
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 9231f41b..9383834f 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -442,6 +442,14 @@ var LibrarySDL = {
}
// fall through
case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
+ // If we preventDefault on keydown events, the subsequent keypress events
+ // won't fire. However, it's fine (and in some cases necessary) to
+ // preventDefault for keys that don't generate a character. Otherwise,
+ // preventDefault is the right thing to do in general.
+ if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) {
+ event.preventDefault();
+ }
+
if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
var button = (event.type == 'DOMMouseScroll' ? event.detail : -event.wheelDelta) > 0 ? 4 : 3;
var event2 = {
@@ -463,7 +471,6 @@ var LibrarySDL = {
// ignore extra ups, can happen if we leave the canvas while pressing down, then return,
// since we add a mouseup in that case
if (!SDL.DOMButtons[event.button]) {
- event.preventDefault();
return;
}
@@ -499,13 +506,6 @@ var LibrarySDL = {
SDL.savedKeydown = event;
}
- // If we preventDefault on keydown events, the subsequent keypress events
- // won't fire. However, it's fine (and in some cases necessary) to
- // preventDefault for keys that don't generate a character.
- if (event.type !== 'keydown' || (event.keyCode === 8 /* backspace */ || event.keyCode === 9 /* tab */)) {
- event.preventDefault();
- }
-
// Don't push keypress events unless SDL_StartTextInput has been called.
if (event.type !== 'keypress' || SDL.textInput) {
SDL.events.push(event);
@@ -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);
@@ -883,6 +883,14 @@ var LibrarySDL = {
surfData.locked++;
if (surfData.locked > 1) return 0;
+ // Mark in C/C++-accessible SDL structure
+ // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
+ // So we have fields all of the same size, and 5 of them before us.
+ // TODO: Use macros like in library.js
+ {{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
+
+ if (surf == SDL.screen && Module.screenIsReadOnly && surfData.image) return 0;
+
surfData.image = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
if (surf == SDL.screen) {
var data = surfData.image.data;
@@ -925,12 +933,6 @@ var LibrarySDL = {
}
}
- // Mark in C/C++-accessible SDL structure
- // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ...
- // So we have fields all of the same size, and 5 of them before us.
- // TODO: Use macros like in library.js
- {{{ makeSetValue('surf', '5*Runtime.QUANTUM_SIZE', 'surfData.buffer', 'void*') }}};
-
return 0;
},
@@ -1271,6 +1273,11 @@ var LibrarySDL = {
return 1;
},
+ SDL_SetPalette__deps: ['SDL_SetColors'],
+ SDL_SetPalette: function(surf, flags, colors, firstColor, nColors) {
+ return _SDL_SetColors(surf, colors, firstColor, nColors);
+ },
+
SDL_MapRGB: function(fmt, r, g, b) {
// Canvas screens are always RGBA. We assume the machine is little-endian.
return r&0xff|(g&0xff)<<8|(b&0xff)<<16|0xff000000;
@@ -2276,7 +2283,7 @@ var LibrarySDL = {
SDL_CondWaitTimeout: function() { throw 'SDL_CondWaitTimeout: TODO' },
SDL_WM_IconifyWindow: function() { throw 'SDL_WM_IconifyWindow TODO' },
- Mix_SetPostMix: function() { throw 'Mix_SetPostMix: TODO' },
+ Mix_SetPostMix: function() { Runtime.warnOnce('Mix_SetPostMix: TODO') },
Mix_QuerySpec: function() { throw 'Mix_QuerySpec: TODO' },
Mix_FadeInChannelTimed: function() { throw 'Mix_FadeInChannelTimed' },
Mix_FadeOutChannel: function() { throw 'Mix_FadeOutChannel' },
diff --git a/src/modules.js b/src/modules.js
index 373e60d9..1a931572 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -227,6 +227,8 @@ var Types = {
needAnalysis: {}, // Types noticed during parsing, that need analysis
+ hasInlineJS: false, // whether the program has inline JS anywhere
+
// Set to true if we actually use precise i64 math: If PRECISE_I64_MATH is set, and also such math is actually
// needed (+,-,*,/,% - we do not need it for bitops), or PRECISE_I64_MATH is 2 (forced)
preciseI64MathUsed: (PRECISE_I64_MATH == 2)
@@ -467,7 +469,10 @@ var PassManager = {
}));
} else if (phase == 'funcs') {
print('\n//FORWARDED_DATA:' + JSON.stringify({
- Types: { preciseI64MathUsed: Types.preciseI64MathUsed },
+ Types: {
+ hasInlineJS: Types.hasInlineJS,
+ preciseI64MathUsed: Types.preciseI64MathUsed
+ },
Functions: {
blockAddresses: Functions.blockAddresses,
indexedFunctions: Functions.indexedFunctions,
diff --git a/src/postamble.js b/src/postamble.js
index df844121..94b60288 100644
--- a/src/postamble.js
+++ b/src/postamble.js
@@ -10,8 +10,8 @@ ExitStatus.prototype = new Error();
ExitStatus.prototype.constructor = ExitStatus;
var initialStackTop;
-
var preloadStartTime = null;
+var calledMain = false;
Module['callMain'] = Module.callMain = function callMain(args) {
assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)');
@@ -70,6 +70,8 @@ Module['callMain'] = Module.callMain = function callMain(args) {
} else {
throw e;
}
+ } finally {
+ calledMain = true;
}
}
@@ -126,7 +128,15 @@ function exit(status) {
// exit the runtime
exitRuntime();
-
+
+ // TODO We should handle this differently based on environment.
+ // In the browser, the best we can do is throw an exception
+ // to halt execution, but in node we could process.exit and
+ // I'd imagine SM shell would have something equivalent.
+ // This would let us set a proper exit status (which
+ // would be great for checking test exit statuses).
+ // https://github.com/kripken/emscripten/issues/1371
+
// throw an exception to halt the current execution
throw new ExitStatus(status);
}
diff --git a/src/preamble.js b/src/preamble.js
index 227b3043..abcd1c67 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -569,6 +569,78 @@ function Pointer_stringify(ptr, /* optional */ length) {
}
Module['Pointer_stringify'] = Pointer_stringify;
+// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns
+// a copy of that string as a Javascript String object.
+function UTF16ToString(ptr) {
+ var i = 0;
+
+ var str = '';
+ while (1) {
+ var codeUnit = {{{ makeGetValue('ptr', 'i*2', 'i16') }}};
+ if (codeUnit == 0)
+ return str;
+ ++i;
+ // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through.
+ str += String.fromCharCode(codeUnit);
+ }
+}
+Module['UTF16ToString'] = UTF16ToString;
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in UTF16LE form. The copy will require at most (str.length*2+1)*2 bytes of space in the HEAP.
+function stringToUTF16(str, outPtr) {
+ for(var i = 0; i < str.length; ++i) {
+ // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP.
+ var codeUnit = str.charCodeAt(i); // possibly a lead surrogate
+ {{{ makeSetValue('outPtr', 'i*2', 'codeUnit', 'i16') }}}
+ }
+ // Null-terminate the pointer to the HEAP.
+ {{{ makeSetValue('outPtr', 'str.length*2', 0, 'i16') }}}
+}
+Module['stringToUTF16'] = stringToUTF16;
+
+// Given a pointer 'ptr' to a null-terminated UTF32LE-encoded string in the emscripten HEAP, returns
+// a copy of that string as a Javascript String object.
+function UTF32ToString(ptr) {
+ var i = 0;
+
+ var str = '';
+ while (1) {
+ var utf32 = {{{ makeGetValue('ptr', 'i*4', 'i32') }}};
+ if (utf32 == 0)
+ return str;
+ ++i;
+ // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing.
+ if (utf32 >= 0x10000) {
+ var ch = utf32 - 0x10000;
+ str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));
+ } else {
+ str += String.fromCharCode(utf32);
+ }
+ }
+}
+Module['UTF32ToString'] = UTF32ToString;
+
+// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',
+// null-terminated and encoded in UTF32LE form. The copy will require at most (str.length+1)*4 bytes of space in the HEAP,
+// but can use less, since str.length does not return the number of characters in the string, but the number of UTF-16 code units in the string.
+function stringToUTF32(str, outPtr) {
+ var iChar = 0;
+ for(var iCodeUnit = 0; iCodeUnit < str.length; ++iCodeUnit) {
+ // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.
+ var codeUnit = str.charCo