diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-09-12 14:47:17 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-09-12 14:47:17 -0700 |
commit | 6010666be99cd0322babba1174cfbc65c776deb5 (patch) | |
tree | dad63b03b751394c169de61fbf2c195f4faf5344 | |
parent | 38890204ed1f5f8dd34cced7c42fc9cf42dccab5 (diff) | |
parent | f9dff9b3f2e95b2ca8e5b8fd97538f301fd080fe (diff) |
Merge branch 'incoming'
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) @@ -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; |