summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/Platform/Emscripten.cmake106
-rw-r--r--cmake/Platform/Emscripten_unix.cmake24
-rwxr-xr-xemcc51
-rwxr-xr-xemlibtool11
-rw-r--r--emlibtool.bat2
-rwxr-xr-xemscripten.py119
-rw-r--r--src/intertyper.js11
-rw-r--r--src/jsifier.js40
-rw-r--r--src/library.js239
-rw-r--r--src/library_fs.js1389
-rw-r--r--src/library_gl.js15
-rw-r--r--src/library_openal.js17
-rw-r--r--src/library_sdl.js53
-rw-r--r--src/library_tty.js32
-rw-r--r--src/modules.js8
-rw-r--r--src/parseTools.js9
-rw-r--r--src/postamble.js4
-rw-r--r--src/preamble.js4
-rw-r--r--src/proxyClient.js71
-rw-r--r--src/proxyWorker.js128
-rw-r--r--src/settings.js10
-rw-r--r--src/shell_sharedlib.js19
-rwxr-xr-xsystem/bin/sdl-config13
-rw-r--r--system/include/libcxx/__locale12
-rw-r--r--system/include/libcxx/locale2
-rw-r--r--system/lib/libcxx/exception.cpp6
-rw-r--r--system/lib/libcxx/locale.cpp24
-rw-r--r--system/lib/libcxxabi/src/cxa_new_delete.cpp2
-rw-r--r--tests/browser_main.cpp42
-rw-r--r--tests/browser_module.cpp15
-rw-r--r--tests/cmake/target_html/CMakeLists.txt8
-rw-r--r--tests/cmake/target_js/CMakeLists.txt24
-rw-r--r--tests/dlmalloc_proxy.c85
-rw-r--r--tests/filesystem/src.js22
-rw-r--r--tests/gl_vertex_buffer.c195
-rw-r--r--tests/gl_vertex_buffer.pngbin0 -> 47668 bytes
-rw-r--r--tests/gl_vertex_buffer_pre.c177
-rw-r--r--tests/gl_vertex_buffer_pre.pngbin0 -> 83534 bytes
-rwxr-xr-xtests/runner.py8
-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.py79
-rw-r--r--tests/test_core.py619
-rw-r--r--tests/test_other.py104
-rw-r--r--tests/unistd/access.c8
-rw-r--r--tests/unistd/access.js4
-rw-r--r--tests/unistd/access.out4
-rw-r--r--tests/unistd/curdir.c11
-rw-r--r--tests/unistd/curdir.js7
-rw-r--r--tests/unistd/io.c50
-rw-r--r--tests/unistd/io.js52
-rw-r--r--tests/unistd/links.c7
-rw-r--r--tests/unistd/links.js3
-rw-r--r--tests/unistd/truncate.c7
-rw-r--r--tests/unistd/truncate.js2
-rw-r--r--tools/js-optimizer.js25
-rw-r--r--tools/jsrun.py1
-rw-r--r--tools/shared.py144
-rw-r--r--tools/test-js-optimizer-asm-outline1-output.js133
-rw-r--r--tools/test-js-optimizer-asm-outline1.js40
61 files changed, 3106 insertions, 1288 deletions
diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake
index aafd38a8..9bfa829d 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 "")
- set(CMAKE_FIND_ROOT_PATH "$ENV{EMSCRIPTEN}")
+ 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 "")
+ 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")
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 a07ce4f1..48aa19e7 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:
@@ -663,6 +666,12 @@ if '-M' in sys.argv or '-MM' in sys.argv:
logging.debug('just dependencies: ' + ' '.join(cmd))
exit(subprocess.call(cmd))
+if '-E' in sys.argv:
+ # Just run the preprocessor
+ cmd = [CC] + sys.argv[1:]
+ logging.debug('just preprocssor ' + ' '.join(cmd))
+ exit(subprocess.call(cmd))
+
# Check if a target is specified
target = None
for i in range(len(sys.argv)-1):
@@ -734,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.
@@ -897,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):
@@ -1070,9 +1083,6 @@ try:
shared.Settings.CORRECT_OVERFLOWS = 1
assert not shared.Settings.PGO, 'cannot run PGO in ASM_JS mode'
- if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
- logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
-
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
debug_level = 4 # must keep debug info to do line-by-line operations
@@ -1107,13 +1117,27 @@ try:
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
debug_level = max(debug_level, 2)
- if shared.Settings.DLOPEN_SUPPORT:
- shared.Settings.LINKABLE = 1
+ if shared.Settings.ASSERTIONS and shared.Settings.ALIASING_FUNCTION_POINTERS:
+ logging.warning('ALIASING_FUNCTION_POINTERS is on, function pointer comparisons may be invalid across types')
if shared.Settings.STB_IMAGE and final_suffix in JS_CONTAINING_SUFFIXES:
input_files.append(shared.path_from_root('third_party', 'stb_image.c'))
shared.Settings.EXPORTED_FUNCTIONS += ['_stbi_load', '_stbi_load_from_memory', '_stbi_image_free']
+ if type(shared.Settings.EXPORTED_FUNCTIONS) in (list, tuple):
+ # always need malloc and free to be kept alive and exported, for internal use and other modules
+ for required_export in ['_malloc', '_free']:
+ if required_export not in shared.Settings.EXPORTED_FUNCTIONS:
+ shared.Settings.EXPORTED_FUNCTIONS.append(required_export)
+ else:
+ logging.debug('using response file for EXPORTED_FUNCTIONS, make sure it includes _malloc and _free')
+
+ 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')
@@ -1695,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)
@@ -1769,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/emlibtool b/emlibtool
deleted file mode 100755
index 1eb18edc..00000000
--- a/emlibtool
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python2
-
-'''
-This is a helper script. See emcc.
-'''
-
-import os, sys
-from tools import shared
-
-raise Exception('TODO: emlibtool')
-
diff --git a/emlibtool.bat b/emlibtool.bat
deleted file mode 100644
index 4ea705be..00000000
--- a/emlibtool.bat
+++ /dev/null
@@ -1,2 +0,0 @@
-@echo off
-python "%~dp0\emlibtool" %* \ No newline at end of file
diff --git a/emscripten.py b/emscripten.py
index c5e235d8..fcf109bf 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -287,6 +287,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
@@ -444,6 +445,11 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
forwarded_json['Functions']['libraryFunctions'].get('llvm_ctlz_i32'):
basic_vars += ['cttz_i8', 'ctlz_i8']
+ if settings.get('DLOPEN_SUPPORT'):
+ for sig in last_forwarded_json['Functions']['tables'].iterkeys():
+ basic_vars.append('F_BASE_%s' % sig)
+ asm_setup += ' var F_BASE_%s = %s;\n' % (sig, 'FUNCTION_TABLE_OFFSET' if settings.get('SIDE_MODULE') else '0') + '\n'
+
asm_runtime_funcs = ['stackAlloc', 'stackSave', 'stackRestore', 'setThrew'] + ['setTempRet%d' % i for i in range(10)]
# function tables
def asm_coerce(value, sig):
@@ -475,8 +481,12 @@ def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
''' % (sig, i, args, arg_coercions, jsret))
from tools import shared
+ shared.Settings.copy(settings)
asm_setup += '\n' + shared.JS.make_invoke(sig) + '\n'
basic_funcs.append('invoke_%s' % sig)
+ if settings.get('DLOPEN_SUPPORT'):
+ asm_setup += '\n' + shared.JS.make_extcall(sig) + '\n'
+ basic_funcs.append('extCall_%s' % sig)
# calculate exports
exported_implemented_functions = list(exported_implemented_functions)
@@ -534,7 +544,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);
@@ -543,7 +553,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;
@@ -606,23 +616,36 @@ function setTempRet%d(value) {
// EMSCRIPTEN_END_ASM
(%s, %s, buffer);
%s;
+''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)]
+
+ if not settings.get('SIDE_MODULE'):
+ funcs_js.append('''
Runtime.stackAlloc = function(size) { return asm['stackAlloc'](size) };
Runtime.stackSave = function() { return asm['stackSave']() };
Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
-''' % (pre_tables + '\n'.join(function_tables_impls) + '\n' + function_tables_defs.replace('\n', '\n '), exports, the_global, sending, receiving)]
+''')
# Set function table masks
- def function_table_maskize(js):
- masks = {}
- default = None
- for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
- masks[sig] = str(table.count(','))
- default = sig
+ masks = {}
+ max_mask = 0
+ for sig, table in last_forwarded_json['Functions']['tables'].iteritems():
+ mask = table.count(',')
+ masks[sig] = str(mask)
+ max_mask = max(mask, max_mask)
+ def function_table_maskize(js, masks):
def fix(m):
sig = m.groups(0)[0]
return masks[sig]
return re.sub(r'{{{ FTM_([\w\d_$]+) }}}', lambda m: fix(m), js) # masks[m.groups(0)[0]]
- funcs_js = map(function_table_maskize, funcs_js)
+ funcs_js = map(lambda js: function_table_maskize(js, masks), funcs_js)
+
+ if settings.get('DLOPEN_SUPPORT'):
+ funcs_js.append('''
+ asm.maxFunctionIndex = %(max_mask)d;
+ DLFCN.registerFunctions(asm, %(max_mask)d+1, %(sigs)s, Module);
+ Module.SYMBOL_TABLE = SYMBOL_TABLE;
+''' % { 'max_mask': max_mask, 'sigs': str(map(str, last_forwarded_json['Functions']['tables'].keys())) })
+
else:
function_tables_defs = '\n'.join([table for table in last_forwarded_json['Functions']['tables'].itervalues()])
outfile.write(function_tables_defs)
@@ -637,13 +660,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
symbol_table = {}
for k, v in forwarded_json['Variables']['indexedGlobals'].iteritems():
if forwarded_json['Variables']['globals'][k]['named']:
- symbol_table[k] = v + forwarded_json['Runtime']['GLOBAL_BASE']
+ symbol_table[k] = str(v + forwarded_json['Runtime']['GLOBAL_BASE'])
for raw in last_forwarded_json['Functions']['tables'].itervalues():
if raw == '': continue
table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(","))
- symbol_table.update(map(lambda x: (x[1], x[0]),
- filter(lambda x: x[1] != '0', enumerate(table))))
- outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table))
+ for i in range(len(table)):
+ value = table[i]
+ if value != '0':
+ if settings.get('SIDE_MODULE'):
+ symbol_table[value] = 'FUNCTION_TABLE_OFFSET+' + str(i)
+ else:
+ symbol_table[value] = str(i)
+ outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table).replace('"', ''))
for funcs_js_item in funcs_js: # do this loop carefully to save memory
funcs_js_item = indexize(funcs_js_item)
@@ -663,69 +691,6 @@ def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBU
name, value = setting.strip().split('=', 1)
settings[name] = json.loads(value)
- # Add header defines to settings
- defines = {}
- include_root = path_from_root('system', 'include')
- headers = args.headers[0].split(',') if len(args.headers) > 0 else []
- seen_headers = set()
- while len(headers) > 0:
- header = headers.pop(0)
- if not os.path.isabs(header):
- header = os.path.join(include_root, header)
- seen_headers.add(header)
- for line in open(header, 'r'):
- line = line.replace('\t', ' ')
- m = re.match('^ *# *define +(?P<name>[-\w_.]+) +\(?(?P<value>[-\w_.|]+)\)?.*', line)
- if not m:
- # Catch enum defines of a very limited sort
- m = re.match('^ +(?P<name>[A-Z_\d]+) += +(?P<value>\d+).*', line)
- if m:
- if m.group('name') != m.group('value'):
- defines[m.group('name')] = m.group('value')
- #else:
- # print 'Warning: %s #defined to itself' % m.group('name') # XXX this can happen if we are set to be equal to an enum (with the same name)
- m = re.match('^ *# *include *["<](?P<name>[\w_.-/]+)[">].*', line)
- if m:
- # Find this file
- found = False
- for w in [w for w in os.walk(include_root)]:
- for f in w[2]:
- curr = os.path.join(w[0], f)
- if curr.endswith(m.group('name')) and curr not in seen_headers:
- headers.append(curr)
- found = True
- break
- if found: break
- #assert found, 'Could not find header: ' + m.group('name')
- if len(defines) > 0:
- def lookup(value):
- try:
- while not unicode(value).isnumeric():
- value = defines[value]
- return value
- except:
- pass
- try: # 0x300 etc.
- value = eval(value)
- return value
- except:
- pass
- try: # CONST1|CONST2
- parts = map(lookup, value.split('|'))
- value = reduce(lambda a, b: a|b, map(eval, parts))
- return value
- except:
- pass
- return None
- for key, value in defines.items():
- value = lookup(value)
- if value is not None:
- defines[key] = str(value)
- else:
- del defines[key]
- #print >> sys.stderr, 'new defs:', str(defines).replace(',', ',\n '), '\n\n'
- settings.setdefault('C_DEFINES', {}).update(defines)
-
# libraries
libraries = args.libraries[0].split(',') if len(args.libraries) > 0 else []
diff --git a/src/intertyper.js b/src/intertyper.js
index 31e97bd0..f9633549 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -707,16 +707,25 @@ function intertyper(data, sidePass, baseLineNums) {
var tokensLeft = item.tokens.slice(2);
item.ident = eatLLVMIdent(tokensLeft);
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');
+ }
+ 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
var i = 0;
+ var params = [], args = [];
splitTokenList(tokensLeft[3].item.tokens).map(function(element) {
var ident = toNiceIdent(element[1].text);
var type = element[0].text;
- item.ident = item.ident.replace(new RegExp('\\$' + i++, 'g'), ident);
+ params.push('$' + (i++));
+ args.push(ident);
});
+ if (item.assignTo) item.ident = 'return ' + item.ident;
+ item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');';
return { forward: null, ret: [item], item: item };
}
if (item.ident.substr(-2) == '()') {
diff --git a/src/jsifier.js b/src/jsifier.js
index da2ba749..a3b26aa9 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';
}
@@ -338,6 +340,7 @@ function JSify(data, functionsOnly, givenFunctions) {
// External variables in shared libraries should not be declared as
// they would shadow similarly-named globals in the parent, so do nothing here.
if (BUILD_AS_SHARED_LIB) return ret;
+ if (SIDE_MODULE) return [];
// Library items need us to emit something, but everything else requires nothing.
if (!LibraryManager.library[item.ident.slice(1)]) return ret;
}
@@ -1142,8 +1145,8 @@ function JSify(data, functionsOnly, givenFunctions) {
});
var range = maxx - minn;
var useIfs = (item.switchLabels.length+1) < 6 || range > 10*1024 || (range/item.switchLabels.length) > 1024; // heuristics
- if (VERBOSE && useIfs && item.switchLabels.length > 2) {
- warn('not optimizing llvm switch into js switch because ' + [range, range/item.switchLabels.length]);
+ if (VERBOSE && useIfs && item.switchLabels.length >= 6) {
+ warn('not optimizing llvm switch into js switch because range of values is ' + range + ', density is ' + range/item.switchLabels.length);
}
var phiSets = calcPhiSets(item);
@@ -1405,7 +1408,10 @@ function JSify(data, functionsOnly, givenFunctions) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
+ var extCall = false;
+
if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call
+ if (ASM_JS && DLOPEN_SUPPORT && !invoke && !funcData.setjmpTable) extCall = true; // go out, to be able to access other modules TODO: optimize
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
@@ -1466,7 +1472,7 @@ function JSify(data, functionsOnly, givenFunctions) {
args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
if (ASM_JS) {
- if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) {
+ if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || extCall || funcData.setjmpTable) {
args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
} else {
args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) });
@@ -1556,17 +1562,17 @@ function JSify(data, functionsOnly, givenFunctions) {
var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out)
- var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke;
+ Functions.neededTables[sig] = 1;
+ var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke && !extCall;
if (functionTableCall) {
// normal asm function pointer call
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
- Functions.neededTables[sig] = 1;
} else {
- // This is a call through an invoke_*, either a forced one, or a setjmp-required one
+ // This is a call through an invoke_* or extCall, either a forced one, or a setjmp-required one
// note: no need to update argsTypes at this point
if (byPointerForced) Functions.unimplementedFunctions[callIdent] = sig;
args.unshift(byPointerForced ? Functions.getIndex(callIdent, sig) : asmCoercion(callIdent, 'i32'));
- callIdent = 'invoke_' + sig;
+ callIdent = (extCall ? 'extCall' : 'invoke') + '_' + sig;
}
} else if (SAFE_DYNCALLS) {
assert(!ASM_JS, 'cannot emit safe dyncalls in asm');
@@ -1660,8 +1666,13 @@ function JSify(data, functionsOnly, givenFunctions) {
Variables.generatedGlobalBase = true;
// Globals are done, here is the rest of static memory
assert((TARGET_LE32 && Runtime.GLOBAL_BASE == 8) || (TARGET_X86 && Runtime.GLOBAL_BASE == 4)); // this is assumed in e.g. relocations for linkable modules
- print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n');
- print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n');
+ if (!SIDE_MODULE) {
+ print('STATIC_BASE = ' + Runtime.GLOBAL_BASE + ';\n');
+ print('STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n');
+ } else {
+ print('H_BASE = parentModule["_malloc"](' + Runtime.alignMemory(Variables.nextIndexedOffset) + ' + Runtime.GLOBAL_BASE);\n');
+ print('// STATICTOP = STATIC_BASE + ' + Runtime.alignMemory(Variables.nextIndexedOffset) + ';\n'); // comment as metadata only
+ }
}
var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable);
print(generated.map(function(item) { return item.JS; }).join('\n'));
@@ -1688,7 +1699,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return true;
});
// write out the singleton big memory initialization value
- print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE', true));
+ print('/* memory initializer */ ' + makePointer(memoryInitialization, null, 'ALLOC_NONE', 'i8', 'Runtime.GLOBAL_BASE' + (SIDE_MODULE ? '+H_BASE' : ''), true));
} else {
print('/* no memory initializer */'); // test purposes
}
@@ -1700,7 +1711,7 @@ function JSify(data, functionsOnly, givenFunctions) {
print('}\n');
if (USE_TYPED_ARRAYS == 2) {
- if (!BUILD_AS_SHARED_LIB) {
+ if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('var tempDoublePtr = Runtime.alignMemory(allocate(12, "i8", ALLOC_STATIC), 8);\n');
print('assert(tempDoublePtr % 8 == 0);\n');
print('function copyTempFloat(ptr) { // functions, because inlining this code increases code size too much\n');
@@ -1754,7 +1765,7 @@ function JSify(data, functionsOnly, givenFunctions) {
legalizedI64s = legalizedI64sDefault;
- if (!BUILD_AS_SHARED_LIB) {
+ if (!BUILD_AS_SHARED_LIB && !SIDE_MODULE) {
print('STACK_BASE = STACKTOP = Runtime.alignMemory(STATICTOP);\n');
print('staticSealed = true; // seal the static portion of memory\n');
print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n');
@@ -1832,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.js b/src/library.js
index 9d0c65d5..f3c3c1ec 100644
--- a/src/library.js
+++ b/src/library.js
@@ -323,10 +323,16 @@ LibraryManager.library = {
path = Pointer_stringify(path);
// we don't want this in the JS API as the JS API
// uses mknod to create all nodes.
- var err = FS.mayMknod(mode);
- if (err) {
- ___setErrNo(err);
- return -1;
+ switch (mode & {{{ cDefine('S_IFMT') }}}) {
+ case {{{ cDefine('S_IFREG') }}}:
+ case {{{ cDefine('S_IFCHR') }}}:
+ case {{{ cDefine('S_IFBLK') }}}:
+ case {{{ cDefine('S_IFIFO') }}}:
+ case {{{ cDefine('S_IFSOCK') }}}:
+ break;
+ default:
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
}
try {
FS.mknod(path, mode, dev);
@@ -4273,9 +4279,11 @@ LibraryManager.library = {
__cxa_guard_release: function() {},
__cxa_guard_abort: function() {},
+#if USE_TYPED_ARRAYS != 2
_ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer
_ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes
_ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes
+#endif
// Exceptions
__cxa_allocate_exception: function(size) {
@@ -4657,20 +4665,28 @@ LibraryManager.library = {
cos: 'Math.cos',
cosf: 'Math.cos',
+ cosl: 'Math.cos',
sin: 'Math.sin',
sinf: 'Math.sin',
+ sinl: 'Math.sin',
tan: 'Math.tan',
tanf: 'Math.tan',
+ tanl: 'Math.tan',
acos: 'Math.acos',
acosf: 'Math.acos',
+ acosl: 'Math.acos',
asin: 'Math.asin',
asinf: 'Math.asin',
+ asinl: 'Math.asin',
atan: 'Math.atan',
atanf: 'Math.atan',
+ atanl: 'Math.atan',
atan2: 'Math.atan2',
atan2f: 'Math.atan2',
+ atan2l: 'Math.atan2',
exp: 'Math.exp',
expf: 'Math.exp',
+ expl: 'Math.exp',
// The erf and erfc functions are inspired from
// http://www.digitalmars.com/archives/cplusplus/3634.html
@@ -4706,7 +4722,8 @@ LibraryManager.library = {
} while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE);
return (ONE_SQRTPI * Math.exp(- x * x) * q2);
},
- erfcf: 'erfcf',
+ erfcf: 'erfc',
+ erfcl: 'erfc',
erf__deps: ['erfc'],
erf: function(x) {
var MATH_TOLERANCE = 1E-12;
@@ -4730,18 +4747,25 @@ LibraryManager.library = {
return (TWO_SQRTPI * sum);
},
erff: 'erf',
+ erfl: 'erf',
log: 'Math.log',
logf: 'Math.log',
+ logl: 'Math.log',
sqrt: 'Math.sqrt',
sqrtf: 'Math.sqrt',
+ sqrtl: 'Math.sqrt',
fabs: 'Math.abs',
fabsf: 'Math.abs',
+ fabsl: 'Math.abs',
ceil: 'Math.ceil',
ceilf: 'Math.ceil',
+ ceill: 'Math.ceil',
floor: 'Math.floor',
floorf: 'Math.floor',
+ floorl: 'Math.floor',
pow: 'Math.pow',
powf: 'Math.pow',
+ powl: 'Math.pow',
llvm_sqrt_f32: 'Math.sqrt',
llvm_sqrt_f64: 'Math.sqrt',
llvm_pow_f32: 'Math.pow',
@@ -4812,6 +4836,7 @@ LibraryManager.library = {
return __reallyNegative(a) === __reallyNegative(b) ? a : -a;
},
copysignf: 'copysign',
+ copysignl: 'copysign',
__signbit__deps: ['copysign'],
__signbit: function(x) {
// We implement using copysign so that we get support
@@ -4824,56 +4849,70 @@ LibraryManager.library = {
return Math.sqrt(a*a + b*b);
},
hypotf: 'hypot',
+ hypotl: 'hypot',
sinh: function(x) {
var p = Math.pow(Math.E, x);
return (p - (1 / p)) / 2;
},
sinhf: 'sinh',
+ sinhl: 'sinh',
cosh: function(x) {
var p = Math.pow(Math.E, x);
return (p + (1 / p)) / 2;
},
coshf: 'cosh',
+ coshl: 'cosh',
tanh__deps: ['sinh', 'cosh'],
tanh: function(x) {
return _sinh(x) / _cosh(x);
},
tanhf: 'tanh',
+ tanhl: 'tanh',
asinh: function(x) {
return Math.log(x + Math.sqrt(x * x + 1));
},
asinhf: 'asinh',
+ asinhl: 'asinh',
acosh: function(x) {
return Math.log(x * 1 + Math.sqrt(x * x - 1));
},
acoshf: 'acosh',
+ acoshl: 'acosh',
atanh: function(x) {
return Math.log((1 + x) / (1 - x)) / 2;
},
atanhf: 'atanh',
+ atanhl: 'atanh',
exp2: function(x) {
return Math.pow(2, x);
},
exp2f: 'exp2',
+ exp2l: 'exp2',
expm1: function(x) {
return Math.exp(x) - 1;
},
expm1f: 'expm1',
+ expm1l: 'expm1',
round: function(x) {
return (x < 0) ? -Math.round(-x) : Math.round(x);
},
roundf: 'round',
+ roundl: 'round',
lround: 'round',
lroundf: 'round',
+ lroundl: 'round',
llround: 'round',
llroundf: 'round',
+ llroundl: 'round',
rint: function(x) {
if (Math.abs(x % 1) !== 0.5) return Math.round(x);
return x + x % 2 + ((x < 0) ? 1 : -1);
},
rintf: 'rint',
+ rintl: 'rint',
lrint: 'rint',
lrintf: 'rint',
+ lrintl: 'rint',
#if USE_TYPED_ARRAYS == 2
llrint: function(x) {
x = (x < 0) ? -Math.round(-x) : Math.round(x);
@@ -4883,50 +4922,63 @@ LibraryManager.library = {
llrint: 'rint',
#endif
llrintf: 'llrint',
+ llrintl: 'llrint',
nearbyint: 'rint',
nearbyintf: 'rint',
+ nearbyintl: 'rint',
trunc: function(x) {
return (x < 0) ? Math.ceil(x) : Math.floor(x);
},
truncf: 'trunc',
+ truncl: 'trunc',
fdim: function(x, y) {
return (x > y) ? x - y : 0;
},
fdimf: 'fdim',
+ fdiml: 'fdim',
fmax: function(x, y) {
return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y);
},
fmaxf: 'fmax',
+ fmaxl: 'fmax',
fmin: function(x, y) {
return isNaN(x) ? y : isNaN(y) ? x : Math.min(x, y);
},
fminf: 'fmin',
+ fminl: 'fmin',
fma: function(x, y, z) {
return x * y + z;
},
fmaf: 'fma',
+ fmal: 'fma',
fmod: function(x, y) {
return x % y;
},
fmodf: 'fmod',
+ fmodl: 'fmod',
remainder: 'fmod',
remainderf: 'fmod',
+ remainderl: 'fmod',
log10: function(x) {
return Math.log(x) / Math.LN10;
},
log10f: 'log10',
+ log10l: 'log10',
log1p: function(x) {
return Math.log(1 + x);
},
log1pf: 'log1p',
+ log1pl: 'log1p',
log2: function(x) {
return Math.log(x) / Math.LN2;
},
log2f: 'log2',
+ log2l: 'log2',
nan: function(x) {
return NaN;
},
nanf: 'nan',
+ nanl: 'nan',
sincos: function(x, sine, cosine) {
var sineVal = Math.sin(x),
@@ -4934,6 +4986,7 @@ LibraryManager.library = {
{{{ makeSetValue('sine', '0', 'sineVal', 'double') }}};
{{{ makeSetValue('cosine', '0', 'cosineVal', 'double') }}};
},
+ sincosl: 'sincos',
sincosf: function(x, sine, cosine) {
var sineVal = Math.sin(x),
@@ -5012,24 +5065,74 @@ LibraryManager.library = {
// being compiled. Not sure how to tell LLVM to not do so.
// ==========================================================================
- // Data for dlfcn.h.
- $DLFCN_DATA: {
+ $DLFCN: {
+#if DLOPEN_SUPPORT
+ // extra asm.js dlopen support
+ functionTable: [], // will contain objects mapping sigs to js functions that call into the right asm module with the right index
+
+ registerFunctions: function(asm, num, sigs, jsModule) {
+ // use asm module dynCall_* from functionTable
+ if (num % 2 == 1) num++; // keep pointers even
+ var table = DLFCN.functionTable;
+ var from = table.length;
+ assert(from % 2 == 0);
+ for (var i = 0; i < num; i++) {
+ table[from + i] = {};
+ sigs.forEach(function(sig) { // TODO: new Function etc.
+ var full = 'dynCall_' + sig;
+ table[from + i][sig] = function() {
+ arguments[0] -= from;
+ return asm[full].apply(null, arguments);
+ }
+ });
+ }
+
+ if (jsModule.cleanups) {
+ var newLength = table.length;
+ jsModule.cleanups.push(function() {
+ if (table.length === newLength) {
+ table.length = from; // nothing added since, just shrink
+ } else {
+ // something was added above us, clear and leak the span
+ for (var i = 0; i < num; i++) {
+ table[from + i] = null;
+ }
+ }
+ while (table.length > 0 && table[table.length-1] === null) table.pop();
+ });
+ }
+
+ // patch js module dynCall_* to use functionTable
+ sigs.forEach(function(sig) {
+ jsModule['dynCall_' + sig] = function() {
+ return table[arguments[0]][sig].apply(null, arguments);
+ };
+ });
+ },
+#endif
+
error: null,
errorMsg: null,
loadedLibs: {}, // handle -> [refcount, name, lib_object]
loadedLibNames: {}, // name -> handle
},
// void* dlopen(const char* filename, int flag);
- dlopen__deps: ['$DLFCN_DATA', '$FS', '$ENV'],
+ dlopen__deps: ['$DLFCN', '$FS', '$ENV'],
dlopen: function(filename, flag) {
// void *dlopen(const char *file, int mode);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
- if (DLFCN_DATA.loadedLibNames[filename]) {
+#if ASM_JS
+#if DLOPEN_SUPPORT == 0
+ abort('need to build with DLOPEN_SUPPORT=1 to get dlopen support in asm.js');
+#endif
+#endif
+
+ if (DLFCN.loadedLibNames[filename]) {
// Already loaded; increment ref count and return.
- var handle = DLFCN_DATA.loadedLibNames[filename];
- DLFCN_DATA.loadedLibs[handle].refcount++;
+ var handle = DLFCN.loadedLibNames[filename];
+ DLFCN.loadedLibs[handle].refcount++;
return handle;
}
@@ -5040,7 +5143,7 @@ LibraryManager.library = {
} else {
var target = FS.findObject(filename);
if (!target || target.isFolder || target.isDevice) {
- DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
+ DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename;
return 0;
} else {
FS.forceLoadFile(target);
@@ -5048,19 +5151,26 @@ LibraryManager.library = {
}
try {
- var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
+ var lib_module = eval(lib_data)(
+#if ASM_JS
+ DLFCN.functionTable.length,
+#else
+ {{{ Functions.getTable('x') }}}.length,
+#endif
+ Module
+ );
} catch (e) {
#if ASSERTIONS
Module.printErr('Error in loading dynamic library: ' + e);
#endif
- DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
+ DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
return 0;
}
// Not all browsers support Object.keys().
var handle = 1;
- for (var key in DLFCN_DATA.loadedLibs) {
- if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
+ for (var key in DLFCN.loadedLibs) {
+ if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++;
}
// We don't care about RTLD_NOW and RTLD_LAZY.
@@ -5074,60 +5184,66 @@ LibraryManager.library = {
var cached_functions = {};
}
- DLFCN_DATA.loadedLibs[handle] = {
+ DLFCN.loadedLibs[handle] = {
refcount: 1,
name: filename,
module: lib_module,
cached_functions: cached_functions
};
- DLFCN_DATA.loadedLibNames[filename] = handle;
+ DLFCN.loadedLibNames[filename] = handle;
return handle;
},
// int dlclose(void* handle);
- dlclose__deps: ['$DLFCN_DATA'],
+ dlclose__deps: ['$DLFCN'],
dlclose: function(handle) {
// int dlclose(void *handle);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html
- if (!DLFCN_DATA.loadedLibs[handle]) {
- DLFCN_DATA.errorMsg = 'Tried to dlclose() unopened handle: ' + handle;
+ if (!DLFCN.loadedLibs[handle]) {
+ DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle;
return 1;
} else {
- var lib_record = DLFCN_DATA.loadedLibs[handle];
+ var lib_record = DLFCN.loadedLibs[handle];
if (--lib_record.refcount == 0) {
- delete DLFCN_DATA.loadedLibNames[lib_record.name];
- delete DLFCN_DATA.loadedLibs[handle];
+ if (lib_record.module.cleanups) {
+ lib_record.module.cleanups.forEach(function(cleanup) { cleanup() });
+ }
+ delete DLFCN.loadedLibNames[lib_record.name];
+ delete DLFCN.loadedLibs[handle];
}
return 0;
}
},
// void* dlsym(void* handle, const char* symbol);
- dlsym__deps: ['$DLFCN_DATA'],
+ dlsym__deps: ['$DLFCN'],
dlsym: function(handle, symbol) {
// void *dlsym(void *restrict handle, const char *restrict name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
symbol = '_' + Pointer_stringify(symbol);
- if (!DLFCN_DATA.loadedLibs[handle]) {
- DLFCN_DATA.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle;
+ if (!DLFCN.loadedLibs[handle]) {
+ DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle;
return 0;
} else {
- var lib = DLFCN_DATA.loadedLibs[handle];
+ var lib = DLFCN.loadedLibs[handle];
// self-dlopen means that lib.module is not a superset of
// cached_functions, so check the latter first
if (lib.cached_functions.hasOwnProperty(symbol)) {
return lib.cached_functions[symbol];
} else {
if (!lib.module.hasOwnProperty(symbol)) {
- DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
+ DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
'" in dynamic lib: ' + lib.name);
return 0;
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
- {{{ Functions.getTable('x') }}}.push(result);
- {{{ Functions.getTable('x') }}}.push(0);
- result = {{{ Functions.getTable('x') }}}.length - 2;
+#if ASM_JS
+ result = lib.module.SYMBOL_TABLE[symbol];
+ assert(result);
+#else
+ result = Runtime.addFunction(result);
+#endif
lib.cached_functions = result;
}
return result;
@@ -5136,18 +5252,18 @@ LibraryManager.library = {
}
},
// char* dlerror(void);
- dlerror__deps: ['$DLFCN_DATA'],
+ dlerror__deps: ['$DLFCN'],
dlerror: function() {
// char *dlerror(void);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html
- if (DLFCN_DATA.errorMsg === null) {
+ if (DLFCN.errorMsg === null) {
return 0;
} else {
- if (DLFCN_DATA.error) _free(DLFCN_DATA.error);
- var msgArr = intArrayFromString(DLFCN_DATA.errorMsg);
- DLFCN_DATA.error = allocate(msgArr, 'i8', ALLOC_NORMAL);
- DLFCN_DATA.errorMsg = null;
- return DLFCN_DATA.error;
+ if (DLFCN.error) _free(DLFCN.error);
+ var msgArr = intArrayFromString(DLFCN.errorMsg);
+ DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL);
+ DLFCN.errorMsg = null;
+ return DLFCN.error;
}
},
@@ -5227,8 +5343,8 @@ LibraryManager.library = {
['i32', 'tm_zone']]),
// Statically allocated time struct.
__tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
- // Statically allocated timezone strings.
- __tm_timezones: {},
+ // Statically allocated timezone string. We only use GMT as a timezone.
+ __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)',
// Statically allocated time strings.
__tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
@@ -5256,7 +5372,7 @@ LibraryManager.library = {
return _gmtime_r(time, ___tm_current);
},
- gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'],
+ gmtime_r__deps: ['__tm_struct_layout', '__tm_timezone'],
gmtime_r: function(time, tmPtr) {
var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
var offsets = ___tm_struct_layout;
@@ -5278,12 +5394,7 @@ LibraryManager.library = {
start.setUTCMilliseconds(0);
var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));
{{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}}
-
- var timezone = "GMT";
- if (!(timezone in ___tm_timezones)) {
- ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
- }
- {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+ {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}}
return tmPtr;
},
@@ -5302,7 +5413,7 @@ LibraryManager.library = {
return _localtime_r(time, ___tm_current);
},
- localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'],
+ localtime_r__deps: ['__tm_struct_layout', '__tm_timezone', 'tzset'],
localtime_r: function(time, tmPtr) {
_tzset();
var offsets = ___tm_struct_layout;
@@ -5323,11 +5434,7 @@ LibraryManager.library = {
var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset());
{{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}}
- var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1];
- if (!(timezone in ___tm_timezones)) {
- ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL);
- }
- {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}}
+ {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezone', 'i32') }}}
return tmPtr;
},
@@ -6009,7 +6116,7 @@ LibraryManager.library = {
// int clock_gettime(clockid_t clk_id, struct timespec *tp);
var now = Date.now();
{{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds
- {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported
+ {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '(now % 1000) * 1000 * 1000', 'i32') }}}; // nanoseconds (really milliseconds)
return 0;
},
clock_settime: function(clk_id, tp) {
@@ -6021,7 +6128,7 @@ LibraryManager.library = {
clock_getres: function(clk_id, res) {
// int clock_getres(clockid_t clk_id, struct timespec *res);
{{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}}
- {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
+ {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '1000 * 1000', 'i32') }}} // resolution is milliseconds
return 0;
},
@@ -7193,7 +7300,25 @@ LibraryManager.library = {
}
},
+ // ==========================================================================
+ // net/if.h
+ // ==========================================================================
+
+ if_nametoindex: function(a) {
+ return 0;
+ },
+ if_indextoname: function(a, b) {
+ return 0;
+ },
+ if_nameindex: function() {
+ return 0;
+ },
+ if_freenameindex: function(a) {
+ },
+
+ // ==========================================================================
// netinet/in.h
+ // ==========================================================================
_in6addr_any:
'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)',
@@ -7312,7 +7437,7 @@ LibraryManager.library = {
var aliasesBuf = _malloc(4);
{{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}
{{{ makeSetValue('ret', '___hostent_struct_layout.h_aliases', 'aliasesBuf', 'i8**') }}}
- var afinet = {{{ cDefine("AF_INET") }}};
+ var afinet = {{{ cDefine('AF_INET') }}};
{{{ makeSetValue('ret', '___hostent_struct_layout.h_addrtype', 'afinet', 'i32') }}}
{{{ makeSetValue('ret', '___hostent_struct_layout.h_length', '4', 'i32') }}}
var addrListBuf = _malloc(12);
diff --git a/src/library_fs.js b/src/library_fs.js
index 5573dc27..4a150d80 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -14,11 +14,10 @@ mergeInto(LibraryManager.library, {
'Module["FS_createDevice"] = FS.createDevice;',
$FS: {
root: null,
- nodes: [null],
devices: [null],
streams: [null],
nextInode: 1,
- name_table: null,
+ nameTable: null,
currentPath: '/',
initialized: false,
// Whether we are currently ignoring permissions. Useful when preparing the
@@ -49,6 +48,76 @@ mergeInto(LibraryManager.library, {
},
//
+ // paths
+ //
+ cwd: function() {
+ return FS.currentPath;
+ },
+ lookupPath: function(path, opts) {
+ path = PATH.resolve(FS.currentPath, path);
+ opts = opts || { recurse_count: 0 };
+
+ if (opts.recurse_count > 8) { // max recursive lookup of 8
+ throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
+ }
+
+ // split the path
+ var parts = PATH.normalizeArray(path.split('/').filter(function(p) {
+ return !!p;
+ }), false);
+
+ // start at the root
+ var current = FS.root;
+ var current_path = '/';
+
+ for (var i = 0; i < parts.length; i++) {
+ var islast = (i === parts.length-1);
+ if (islast && opts.parent) {
+ // stop resolving
+ break;
+ }
+
+ current = FS.lookupNode(current, parts[i]);
+ current_path = PATH.join(current_path, parts[i]);
+
+ // jump to the mount's root node if this is a mountpoint
+ if (FS.isMountpoint(current)) {
+ current = current.mount.root;
+ }
+
+ // follow symlinks
+ // by default, lookupPath will not follow a symlink if it is the final path component.
+ // setting opts.follow = true will override this behavior.
+ if (!islast || opts.follow) {
+ var count = 0;
+ while (FS.isLink(current.mode)) {
+ var link = FS.readlink(current_path);
+ current_path = PATH.resolve(PATH.dirname(current_path), link);
+
+ var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });
+ current = lookup.node;
+
+ if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
+ throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
+ }
+ }
+ }
+ }
+
+ return { path: current_path, node: current };
+ },
+ getPath: function(node) {
+ var path;
+ while (true) {
+ if (FS.isRoot(node)) {
+ return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint;
+ }
+ path = path ? PATH.join(node.name, path) : node.name;
+ node = node.parent;
+ }
+ },
+
+ //
// nodes
//
hashName: function(parentid, name) {
@@ -56,19 +125,19 @@ mergeInto(LibraryManager.library, {
for (var i = 0; i < name.length; i++) {
hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0;
}
- return ((parentid + hash) >>> 0) % FS.name_table.length;
+ return ((parentid + hash) >>> 0) % FS.nameTable.length;
},
hashAddNode: function(node) {
var hash = FS.hashName(node.parent.id, node.name);
- node.name_next = FS.name_table[hash];
- FS.name_table[hash] = node;
+ node.name_next = FS.nameTable[hash];
+ FS.nameTable[hash] = node;
},
hashRemoveNode: function(node) {
var hash = FS.hashName(node.parent.id, node.name);
- if (FS.name_table[hash] === node) {
- FS.name_table[hash] = node.name_next;
+ if (FS.nameTable[hash] === node) {
+ FS.nameTable[hash] = node.name_next;
} else {
- var current = FS.name_table[hash];
+ var current = FS.nameTable[hash];
while (current) {
if (current.name_next === node) {
current.name_next = node.name_next;
@@ -84,7 +153,7 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(err);
}
var hash = FS.hashName(parent.id, name);
- for (var node = FS.name_table[hash]; node; node = node.name_next) {
+ for (var node = FS.nameTable[hash]; node; node = node.name_next) {
if (node.parent.id === parent.id && node.name === name) {
return node;
}
@@ -164,76 +233,6 @@ mergeInto(LibraryManager.library, {
},
//
- // paths
- //
- cwd: function() {
- return FS.currentPath;
- },
- lookupPath: function(path, opts) {
- path = PATH.resolve(FS.currentPath, path);
- opts = opts || { recurse_count: 0 };
-
- if (opts.recurse_count > 8) { // max recursive lookup of 8
- throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
- }
-
- // split the path
- var parts = PATH.normalizeArray(path.split('/').filter(function(p) {
- return !!p;
- }), false);
-
- // start at the root
- var current = FS.root;
- var current_path = '/';
-
- for (var i = 0; i < parts.length; i++) {
- var islast = (i === parts.length-1);
- if (islast && opts.parent) {
- // stop resolving
- break;
- }
-
- current = FS.lookupNode(current, parts[i]);
- current_path = PATH.join(current_path, parts[i]);
-
- // jump to the mount's root node if this is a mountpoint
- if (FS.isMountpoint(current)) {
- current = current.mount.root;
- }
-
- // follow symlinks
- // by default, lookupPath will not follow a symlink if it is the final path component.
- // setting opts.follow = true will override this behavior.
- if (!islast || opts.follow) {
- var count = 0;
- while (FS.isLink(current.mode)) {
- var link = FS.readlink(current_path);
- current_path = PATH.resolve(PATH.dirname(current_path), link);
-
- var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });
- current = lookup.node;
-
- if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX).
- throw new FS.ErrnoError(ERRNO_CODES.ELOOP);
- }
- }
- }
- }
-
- return { path: current_path, node: current };
- },
- getPath: function(node) {
- var path;
- while (true) {
- if (FS.isRoot(node)) {
- return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint;
- }
- path = path ? PATH.join(node.name, path) : node.name;
- node = node.parent;
- }
- },
-
- //
// permissions
//
flagModes: {
@@ -287,18 +286,6 @@ mergeInto(LibraryManager.library, {
mayLookup: function(dir) {
return FS.nodePermissions(dir, 'x');
},
- mayMknod: function(mode) {
- switch (mode & {{{ cDefine('S_IFMT') }}}) {
- case {{{ cDefine('S_IFREG') }}}:
- case {{{ cDefine('S_IFCHR') }}}:
- case {{{ cDefine('S_IFBLK') }}}:
- case {{{ cDefine('S_IFIFO') }}}:
- case {{{ cDefine('S_IFSOCK') }}}:
- return 0;
- default:
- return ERRNO_CODES.EINVAL;
- }
- },
mayCreate: function(dir, name) {
try {
var node = FS.lookupNode(dir, name);
@@ -348,45 +335,6 @@ mergeInto(LibraryManager.library, {
},
//
- // devices
- //
- // each character device consists of a device id + stream operations.
- // when a character device node is created (e.g. /dev/stdin) it is
- // assigned a device id that lets us map back to the actual device.
- // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops.
- // however, once opened, the stream's operations are overridden with
- // the operations of the device its underlying node maps back to.
- chrdev_stream_ops: {
- open: function(stream) {
- var device = FS.getDevice(stream.node.rdev);
- // override node's stream ops with the device's
- stream.stream_ops = device.stream_ops;
- // forward the open call
- if (stream.stream_ops.open) {
- stream.stream_ops.open(stream);
- }
- },
- llseek: function() {
- throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
- }
- },
- major: function(dev) {
- return ((dev) >> 8);
- },
- minor: function(dev) {
- return ((dev) & 0xff);
- },
- makedev: function(ma, mi) {
- return ((ma) << 8 | (mi));
- },
- registerDevice: function(dev, ops) {
- FS.devices[dev] = { stream_ops: ops };
- },
- getDevice: function(dev) {
- return FS.devices[dev];
- },
-
- //
// streams
//
MAX_OPEN_FDS: 4096,
@@ -433,563 +381,46 @@ mergeInto(LibraryManager.library, {
},
//
- // compatibility
+ // devices
//
- getMode: function(canRead, canWrite) {
- var mode = 0;
- if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
- if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}};
- return mode;
- },
- joinPath: function(parts, forceRelative) {
- var path = PATH.join.apply(null, parts);
- if (forceRelative && path[0] == '/') path = path.substr(1);
- return path;
- },
- absolutePath: function(relative, base) {
- return PATH.resolve(base, relative);
- },
- standardizePath: function(path) {
- return PATH.normalize(path);
- },
- findObject: function(path, dontResolveLastLink) {
- var ret = FS.analyzePath(path, dontResolveLastLink);
- if (ret.exists) {
- return ret.object;
- } else {
- ___setErrNo(ret.error);
- return null;
- }
- },
- analyzePath: function(path, dontResolveLastLink) {
- // operate from within the context of the symlink's target
- try {
- var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
- path = lookup.path;
- } catch (e) {
- }
- var ret = {
- isRoot: false, exists: false, error: 0, name: null, path: null, object: null,
- parentExists: false, parentPath: null, parentObject: null
- };
- try {
- var lookup = FS.lookupPath(path, { parent: true });
- ret.parentExists = true;
- ret.parentPath = lookup.path;
- ret.parentObject = lookup.node;
- ret.name = PATH.basename(path);
- lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
- ret.exists = true;
- ret.path = lookup.path;
- ret.object = lookup.node;
- ret.name = lookup.node.name;
- ret.isRoot = lookup.path === '/';
- } catch (e) {
- ret.error = e.errno;
- };
- return ret;
- },
- createFolder: function(parent, name, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(canRead, canWrite);
- return FS.mkdir(path, mode);
- },
- createPath: function(parent, path, canRead, canWrite) {
- parent = typeof parent === 'string' ? parent : FS.getPath(parent);
- var parts = path.split('/').reverse();
- while (parts.length) {
- var part = parts.pop();
- if (!part) continue;
- var current = PATH.join(parent, part);
- try {
- FS.mkdir(current, 0777);
- } catch (e) {
- // ignore EEXIST
- }
- parent = current;
- }
- return current;
- },
- createFile: function(parent, name, properties, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(canRead, canWrite);
- return FS.create(path, mode);
- },
- createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
- var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
- var mode = FS.getMode(canRead, canWrite);
- var node = FS.create(path, mode);
- if (data) {
- if (typeof data === 'string') {
- var arr = new Array(data.length);
- for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
- data = arr;
- }
- // make sure we can write to the file
- FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}});
- var stream = FS.open(path, 'w');
- FS.write(stream, data, 0, data.length, 0, canOwn);
- FS.close(stream);
- FS.chmod(path, mode);
- }
- return node;
- },
- createDevice: function(parent, name, input, output) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- var mode = FS.getMode(!!input, !!output);
- if (!FS.createDevice.major) FS.createDevice.major = 64;
- var dev = FS.makedev(FS.createDevice.major++, 0);
- // Create a fake device that a set of stream ops to emulate
- // the old behavior.
- FS.registerDevice(dev, {
- open: function(stream) {
- stream.seekable = false;
- },
- close: function(stream) {
- // flush any pending line data
- if (output && output.buffer && output.buffer.length) {
- output({{{ charCode('\n') }}});
- }
- },
- read: function(stream, buffer, offset, length, pos /* ignored */) {
- var bytesRead = 0;
- for (var i = 0; i < length; i++) {
- var result;
- try {
- result = input();
- } catch (e) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- if (result === undefined && bytesRead === 0) {
- throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
- }
- if (result === null || result === undefined) break;
- bytesRead++;
- buffer[offset+i] = result;
- }
- if (bytesRead) {
- stream.node.timestamp = Date.now();
- }
- return bytesRead;
- },
- write: function(stream, buffer, offset, length, pos) {
- for (var i = 0; i < length; i++) {
- try {
- output(buffer[offset+i]);
- } catch (e) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- }
- if (length) {
- stream.node.timestamp = Date.now();
- }
- return i;
- }
- });
- return FS.mkdev(path, mode, dev);
- },
- createLink: function(parent, name, target, canRead, canWrite) {
- var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
- return FS.symlink(target, path);
- },
- // Makes sure a file's contents are loaded. Returns whether the file has
- // been loaded successfully. No-op for files that have been loaded already.
- forceLoadFile: function(obj) {
- if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
- var success = true;
- if (typeof XMLHttpRequest !== 'undefined') {
- throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
- } else if (Module['read']) {
- // Command-line.
- try {
- // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
- // read() will try to parse UTF8.
- obj.contents = intArrayFromString(Module['read'](obj.url), true);
- } catch (e) {
- success = false;
- }
- } else {
- throw new Error('Cannot load without read() or XMLHttpRequest.');
- }
- if (!success) ___setErrNo(ERRNO_CODES.EIO);
- return success;
- },
- // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous
- // XHR, which is not possible in browsers except in a web worker! Use preloading,
- // either --preload-file in emcc or FS.createPreloadedFile
- createLazyFile: function(parent, name, url, canRead, canWrite) {
- if (typeof XMLHttpRequest !== 'undefined') {
- if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
- // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
- var LazyUint8Array = function() {
- this.lengthKnown = false;
- this.chunks = []; // Loaded chunks. Index is the chunk number
- }
- LazyUint8Array.prototype.get = function(idx) {
- if (idx > this.length-1 || idx < 0) {
- return undefined;
- }
- var chunkOffset = idx % this.chunkSize;
- var chunkNum = Math.floor(idx / this.chunkSize);
- return this.getter(chunkNum)[chunkOffset];
- }
- LazyUint8Array.prototype.setDataGetter = function(getter) {
- this.getter = getter;
- }
- LazyUint8Array.prototype.cacheLength = function() {
- // Find length
- var xhr = new XMLHttpRequest();
- xhr.open('HEAD', url, false);
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- var datalength = Number(xhr.getResponseHeader("Content-length"));
- var header;
- var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
-#if SMALL_XHR_CHUNKS
- var chunkSize = 1024; // Chunk size in bytes
-#else
- var chunkSize = 1024*1024; // Chunk size in bytes
-#endif
-
- if (!hasByteServing) chunkSize = datalength;
-
- // Function to get a range from the remote URL.
- var doXHR = (function(from, to) {
- if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
- if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
-
- // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
- var xhr = new XMLHttpRequest();
- xhr.open('GET', url, false);
- if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
-
- // Some hints to the browser that we want binary data.
- if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
- if (xhr.overrideMimeType) {
- xhr.overrideMimeType('text/plain; charset=x-user-defined');
- }
-
- xhr.send(null);
- if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
- if (xhr.response !== undefined) {
- return new Uint8Array(xhr.response || []);
- } else {
- return intArrayFromString(xhr.responseText || '', true);
- }
- });
- var lazyArray = this;
- lazyArray.setDataGetter(function(chunkNum) {
- var start = chunkNum * chunkSize;
- var end = (chunkNum+1) * chunkSize - 1; // including this byte
- end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
- lazyArray.chunks[chunkNum] = doXHR(start, end);
- }
- if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
- return lazyArray.chunks[chunkNum];
- });
-
- this._length = datalength;
- this._chunkSize = chunkSize;
- this.lengthKnown = true;
- }
-
- var lazyArray = new LazyUint8Array();
- Object.defineProperty(lazyArray, "length", {
- get: function() {
- if(!this.lengthKnown) {
- this.cacheLength();
- }
- return this._length;
- }
- });
- Object.defineProperty(lazyArray, "chunkSize", {
- get: function() {
- if(!this.lengthKnown) {
- this.cacheLength();
- }
- return this._chunkSize;
- }
- });
-
- var properties = { isDevice: false, contents: lazyArray };
- } else {
- var properties = { isDevice: false, url: url };
- }
-
- var node = FS.createFile(parent, name, properties, canRead, canWrite);
- // This is a total hack, but I want to get this lazy file code out of the
- // core of MEMFS. If we want to keep this lazy file concept I feel it should
- // be its own thin LAZYFS proxying calls to MEMFS.
- if (properties.contents) {
- node.contents = properties.contents;
- } else if (properties.url) {
- node.contents = null;
- node.url = properties.url;
- }
- // override each stream op with one that tries to force load the lazy file first
- var stream_ops = {};
- var keys = Object.keys(node.stream_ops);
- keys.forEach(function(key) {
- var fn = node.stream_ops[key];
- stream_ops[key] = function() {
- if (!FS.forceLoadFile(node)) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- return fn.apply(null, arguments);
- };
- });
- // use a custom read function
- stream_ops.read = function(stream, buffer, offset, length, position) {
- if (!FS.forceLoadFile(node)) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- var contents = stream.node.contents;
- var size = Math.min(contents.length - position, length);
- if (contents.slice) { // normal array
- for (var i = 0; i < size; i++) {
- buffer[offset + i] = contents[position + i];
- }
- } else {
- for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
- buffer[offset + i] = contents.get(position + i);
- }
- }
- return size;
- };
- node.stream_ops = stream_ops;
- return node;
- },
- // Preloads a file asynchronously. You can call this before run, for example in
- // preRun. run will be delayed until this file arrives and is set up.
- // If you call it after run(), you may want to pause the main loop until it
- // completes, if so, you can use the onload parameter to be notified when
- // that happens.
- // In addition to normally creating the file, we also asynchronously preload
- // the browser-friendly versions of it: For an image, we preload an Image
- // element and for an audio, and Audio. These are necessary for SDL_Image
- // and _Mixer to find the files in preloadedImages/Audios.
- // You can also call this with a typed array instead of a url. It will then
- // do preloading for the Image/Audio part, as if the typed array were the
- // result of an XHR that you did manually.
- createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn) {
- Browser.init();
- // TODO we should allow people to just pass in a complete filename instead
- // of parent and name being that we just join them anyways
- var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent;
- function processData(byteArray) {
- function finish(byteArray) {
- if (!dontCreateFile) {
- FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
- }
- if (onload) onload();
- removeRunDependency('cp ' + fullname);
+ // each character device consists of a device id + stream operations.
+ // when a character device node is created (e.g. /dev/stdin) it is
+ // assigned a device id that lets us map back to the actual device.
+ // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops.
+ // however, once opened, the stream's operations are overridden with
+ // the operations of the device its underlying node maps back to.
+ chrdev_stream_ops: {
+ open: function(stream) {
+ var device = FS.getDevice(stream.node.rdev);
+ // override node's stream ops with the device's
+ stream.stream_ops = device.stream_ops;
+ // forward the open call
+ if (stream.stream_ops.open) {
+ stream.stream_ops.open(stream);
}
- var handled = false;
- Module['preloadPlugins'].forEach(function(plugin) {
- if (handled) return;
- if (plugin['canHandle'](fullname)) {
- plugin['handle'](byteArray, fullname, finish, function() {
- if (onerror) onerror();
- removeRunDependency('cp ' + fullname);
- });
- handled = true;
- }
- });
- if (!handled) finish(byteArray);
- }
- addRunDependency('cp ' + fullname);
- if (typeof url == 'string') {
- Browser.asyncLoad(url, function(byteArray) {
- processData(byteArray);
- }, onerror);
- } else {
- processData(url);
- }
- },
-
- indexedDB: function() {
- return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
- },
-
- DB_NAME: function() {
- return 'EM_FS_' + window.location.pathname;
- },
- DB_VERSION: 20,
- DB_STORE_NAME: 'FILE_DATA',
-
- // asynchronously saves a list of files to an IndexedDB. The DB will be created if not already existing.
- saveFilesToDB: function(paths, onload, onerror) {
- onload = onload || function(){};
- onerror = onerror || function(){};
- var indexedDB = FS.indexedDB();
- try {
- var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
- } catch (e) {
- return onerror(e);
+ },
+ llseek: function() {
+ throw new FS.ErrnoError(ERRNO_CODES.ESPIPE);
}
- openRequest.onupgradeneeded = function() {
- console.log('creating db');
- var db = openRequest.result;
- db.createObjectStore(FS.DB_STORE_NAME);
- };
- openRequest.onsuccess = function() {
- var db = openRequest.result;
- var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
- var files = transaction.objectStore(FS.DB_STORE_NAME);
- var ok = 0, fail = 0, total = paths.length;
- function finish() {
- if (fail == 0) onload(); else onerror();
- }
- paths.forEach(function(path) {
- var putRequest = files.put(FS.analyzePath(path).object.contents, path);
- putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() };
- putRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
- });
- transaction.onerror = onerror;
- };
- openRequest.onerror = onerror;
},
-
- // asychronously loads a file from IndexedDB.
- loadFilesFromDB: function(paths, onload, onerror) {
- onload = onload || function(){};
- onerror = onerror || function(){};
- var indexedDB = FS.indexedDB();
- try {
- var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
- } catch (e) {
- return onerror(e);
- }
- openRequest.onupgradeneeded = onerror; // no database to load from
- openRequest.onsuccess = function() {
- var db = openRequest.result;
- try {
- var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
- } catch(e) {
- onerror(e);
- return;
- }
- var files = transaction.objectStore(FS.DB_STORE_NAME);
- var ok = 0, fail = 0, total = paths.length;
- function finish() {
- if (fail == 0) onload(); else onerror();
- }
- paths.forEach(function(path) {
- var getRequest = files.get(path);
- getRequest.onsuccess = function() {
- if (FS.analyzePath(path).exists) {
- FS.unlink(path);
- }
- FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
- ok++;
- if (ok + fail == total) finish();
- };
- getRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
- });
- transaction.onerror = onerror;
- };
- openRequest.onerror = onerror;
- },
-
- //
- // general
- //
- createDefaultDirectories: function() {
- FS.mkdir('/tmp', 0777);
- },
- createDefaultDevices: function() {
- // create /dev
- FS.mkdir('/dev', 0777);
- // setup /dev/null
- FS.registerDevice(FS.makedev(1, 3), {
- read: function() { return 0; },
- write: function() { return 0; }
- });
- FS.mkdev('/dev/null', 0666, FS.makedev(1, 3));
- // setup /dev/tty and /dev/tty1
- // stderr needs to print output using Module['printErr']
- // so we register a second tty just for it.
- TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
- TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
- FS.mkdev('/dev/tty', 0666, FS.makedev(5, 0));
- FS.mkdev('/dev/tty1', 0666, FS.makedev(6, 0));
- // we're not going to emulate the actual shm device,
- // just create the tmp dirs that reside in it commonly
- FS.mkdir('/dev/shm', 0777);
- FS.mkdir('/dev/shm/tmp', 0777);
+ major: function(dev) {
+ return ((dev) >> 8);
},
- createStandardStreams: function() {
- // TODO deprecate the old functionality of a single
- // input / output callback and that utilizes FS.createDevice
- // and instead require a unique set of stream ops
-
- // by default, we symlink the standard streams to the
- // default tty devices. however, if the standard streams
- // have been overwritten we create a unique device for
- // them instead.
- if (Module['stdin']) {
- FS.createDevice('/dev', 'stdin', Module['stdin']);
- } else {
- FS.symlink('/dev/tty', '/dev/stdin');
- }
- if (Module['stdout']) {
- FS.createDevice('/dev', 'stdout', null, Module['stdout']);
- } else {
- FS.symlink('/dev/tty', '/dev/stdout');
- }
- if (Module['stderr']) {
- FS.createDevice('/dev', 'stderr', null, Module['stderr']);
- } else {
- FS.symlink('/dev/tty1', '/dev/stderr');
- }
-
- // open default streams for the stdin, stdout and stderr devices
- var stdin = FS.open('/dev/stdin', 'r');
- {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}};
- assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')');
-
- var stdout = FS.open('/dev/stdout', 'w');
- {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}};
- assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')');
-
- var stderr = FS.open('/dev/stderr', 'w');
- {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}};
- assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')');
+ minor: function(dev) {
+ return ((dev) & 0xff);
},
- staticInit: function() {
- FS.name_table = new Array(4096);
-
- FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
- FS.mount(MEMFS, {}, '/');
-
- FS.createDefaultDirectories();
- FS.createDefaultDevices();
+ makedev: function(ma, mi) {
+ return ((ma) << 8 | (mi));
},
- init: function(input, output, error) {
- assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
- FS.init.initialized = true;
-
- // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
- Module['stdin'] = input || Module['stdin'];
- Module['stdout'] = output || Module['stdout'];
- Module['stderr'] = error || Module['stderr'];
-
- FS.createStandardStreams();
+ registerDevice: function(dev, ops) {
+ FS.devices[dev] = { stream_ops: ops };
},
- quit: function() {
- FS.init.initialized = false;
- for (var i = 0; i < FS.streams.length; i++) {
- var stream = FS.streams[i];
- if (!stream) {
- continue;
- }
- FS.close(stream);
- }
+ getDevice: function(dev) {
+ return FS.devices[dev];
},
//
- // vfs functionality
+ // core
//
mount: function(type, opts, mountpoint) {
var mount = {
@@ -1036,16 +467,22 @@ mergeInto(LibraryManager.library, {
},
// helpers to create specific types of nodes
create: function(path, mode) {
+ mode = mode !== undefined ? mode : 0666;
mode &= {{{ cDefine('S_IALLUGO') }}};
mode |= {{{ cDefine('S_IFREG') }}};
return FS.mknod(path, mode, 0);
},
mkdir: function(path, mode) {
+ mode = mode !== undefined ? mode : 0777;
mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}};
mode |= {{{ cDefine('S_IFDIR') }}};
return FS.mknod(path, mode, 0);
},
mkdev: function(path, mode, dev) {
+ if (typeof(dev) === 'undefined') {
+ dev = mode;
+ mode = 0666;
+ }
mode |= {{{ cDefine('S_IFCHR') }}};
return FS.mknod(path, mode, dev);
},
@@ -1471,6 +908,606 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(ERRNO_CODES.ENOTTY);
}
return stream.stream_ops.ioctl(stream, cmd, arg);
+ },
+ readFile: function(path, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'r';
+ opts.encoding = opts.encoding || 'binary';
+ var ret;
+ var stream = FS.open(path, opts.flags);
+ var stat = FS.stat(path);
+ var length = stat.size;
+ var buf = new Uint8Array(length);
+ FS.read(stream, buf, 0, length, 0);
+ if (opts.encoding === 'utf8') {
+ ret = '';
+ var utf8 = new Runtime.UTF8Processor();
+ for (var i = 0; i < length; i++) {
+ ret += utf8.processCChar(buf[i]);
+ }
+ } else if (opts.encoding === 'binary') {
+ ret = buf;
+ } else {
+ throw new Error('Invalid encoding type "' + opts.encoding + '"');
+ }
+ FS.close(stream);
+ return ret;
+ },
+ writeFile: function(path, data, opts) {
+ opts = opts || {};
+ opts.flags = opts.flags || 'w';
+ opts.encoding = opts.encoding || 'utf8';
+ var stream = FS.open(path, opts.flags, opts.mode);
+ if (opts.encoding === 'utf8') {
+ var utf8 = new Runtime.UTF8Processor();
+ var buf = new Uint8Array(utf8.processJSString(data));
+ FS.write(stream, buf, 0, buf.length, 0);
+ } else if (opts.encoding === 'binary') {
+ FS.write(stream, data, 0, data.length, 0);
+ } else {
+ throw new Error('Invalid encoding type "' + opts.encoding + '"');
+ }
+ FS.close(stream);
+ },
+
+ //
+ // module-level FS code
+ // TODO move to pre/postamble
+ //
+ createDefaultDirectories: function() {
+ FS.mkdir('/tmp');
+ },
+ createDefaultDevices: function() {
+ // create /dev
+ FS.mkdir('/dev');
+ // setup /dev/null
+ FS.registerDevice(FS.makedev(1, 3), {
+ read: function() { return 0; },
+ write: function() { return 0; }
+ });
+ FS.mkdev('/dev/null', FS.makedev(1, 3));
+ // setup /dev/tty and /dev/tty1
+ // stderr needs to print output using Module['printErr']
+ // so we register a second tty just for it.
+ TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);
+ TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);
+ FS.mkdev('/dev/tty', FS.makedev(5, 0));
+ FS.mkdev('/dev/tty1', FS.makedev(6, 0));
+ // we're not going to emulate the actual shm device,
+ // just create the tmp dirs that reside in it commonly
+ FS.mkdir('/dev/shm');
+ FS.mkdir('/dev/shm/tmp');
+ },
+ createStandardStreams: function() {
+ // TODO deprecate the old functionality of a single
+ // input / output callback and that utilizes FS.createDevice
+ // and instead require a unique set of stream ops
+
+ // by default, we symlink the standard streams to the
+ // default tty devices. however, if the standard streams
+ // have been overwritten we create a unique device for
+ // them instead.
+ if (Module['stdin']) {
+ FS.createDevice('/dev', 'stdin', Module['stdin']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdin');
+ }
+ if (Module['stdout']) {
+ FS.createDevice('/dev', 'stdout', null, Module['stdout']);
+ } else {
+ FS.symlink('/dev/tty', '/dev/stdout');
+ }
+ if (Module['stderr']) {
+ FS.createDevice('/dev', 'stderr', null, Module['stderr']);
+ } else {
+ FS.symlink('/dev/tty1', '/dev/stderr');
+ }
+
+ // open default streams for the stdin, stdout and stderr devices
+ var stdin = FS.open('/dev/stdin', 'r');
+ {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}};
+ assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')');
+
+ var stdout = FS.open('/dev/stdout', 'w');
+ {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}};
+ assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')');
+
+ var stderr = FS.open('/dev/stderr', 'w');
+ {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}};
+ assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')');
+ },
+ staticInit: function() {
+ FS.nameTable = new Array(4096);
+
+ FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
+ FS.mount(MEMFS, {}, '/');
+
+ FS.createDefaultDirectories();
+ FS.createDefaultDevices();
+ },
+ init: function(input, output, error) {
+ assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');
+ FS.init.initialized = true;
+
+ // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here
+ Module['stdin'] = input || Module['stdin'];
+ Module['stdout'] = output || Module['stdout'];
+ Module['stderr'] = error || Module['stderr'];
+
+ FS.createStandardStreams();
+ },
+ quit: function() {
+ FS.init.initialized = false;
+ for (var i = 0; i < FS.streams.length; i++) {
+ var stream = FS.streams[i];
+ if (!stream) {
+ continue;
+ }
+ FS.close(stream);
+ }
+ },
+
+ //
+ // old v1 compatibility functions
+ //
+ getMode: function(canRead, canWrite) {
+ var mode = 0;
+ if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}};
+ if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}};
+ return mode;
+ },
+ joinPath: function(parts, forceRelative) {
+ var path = PATH.join.apply(null, parts);
+ if (forceRelative && path[0] == '/') path = path.substr(1);
+ return path;
+ },
+ absolutePath: function(relative, base) {
+ return PATH.resolve(base, relative);
+ },
+ standardizePath: function(path) {
+ return PATH.normalize(path);
+ },
+ findObject: function(path, dontResolveLastLink) {
+ var ret = FS.analyzePath(path, dontResolveLastLink);
+ if (ret.exists) {
+ return ret.object;
+ } else {
+ ___setErrNo(ret.error);
+ return null;
+ }
+ },
+ analyzePath: function(path, dontResolveLastLink) {
+ // operate from within the context of the symlink's target
+ try {
+ var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ path = lookup.path;
+ } catch (e) {
+ }
+ var ret = {
+ isRoot: false, exists: false, error: 0, name: null, path: null, object: null,
+ parentExists: false, parentPath: null, parentObject: null
+ };
+ try {
+ var lookup = FS.lookupPath(path, { parent: true });
+ ret.parentExists = true;
+ ret.parentPath = lookup.path;
+ ret.parentObject = lookup.node;
+ ret.name = PATH.basename(path);
+ lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });
+ ret.exists = true;
+ ret.path = lookup.path;
+ ret.object = lookup.node;
+ ret.name = lookup.node.name;
+ ret.isRoot = lookup.path === '/';
+ } catch (e) {
+ ret.error = e.errno;
+ };
+ return ret;
+ },
+ createFolder: function(parent, name, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.mkdir(path, mode);
+ },
+ createPath: function(parent, path, canRead, canWrite) {
+ parent = typeof parent === 'string' ? parent : FS.getPath(parent);
+ var parts = path.split('/').reverse();
+ while (parts.length) {
+ var part = parts.pop();
+ if (!part) continue;
+ var current = PATH.join(parent, part);
+ try {
+ FS.mkdir(current);
+ } catch (e) {
+ // ignore EEXIST
+ }
+ parent = current;
+ }
+ return current;
+ },
+ createFile: function(parent, name, properties, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(canRead, canWrite);
+ return FS.create(path, mode);
+ },
+ createDataFile: function(parent, name, data, canRead, canWrite, canOwn) {
+ var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;
+ var mode = FS.getMode(canRead, canWrite);
+ var node = FS.create(path, mode);
+ if (data) {
+ if (typeof data === 'string') {
+ var arr = new Array(data.length);
+ for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);
+ data = arr;
+ }
+ // make sure we can write to the file
+ FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}});
+ var stream = FS.open(path, 'w');
+ FS.write(stream, data, 0, data.length, 0, canOwn);
+ FS.close(stream);
+ FS.chmod(path, mode);
+ }
+ return node;
+ },
+ createDevice: function(parent, name, input, output) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ var mode = FS.getMode(!!input, !!output);
+ if (!FS.createDevice.major) FS.createDevice.major = 64;
+ var dev = FS.makedev(FS.createDevice.major++, 0);
+ // Create a fake device that a set of stream ops to emulate
+ // the old behavior.
+ FS.registerDevice(dev, {
+ open: function(stream) {
+ stream.seekable = false;
+ },
+ close: function(stream) {
+ // flush any pending line data
+ if (output && output.buffer && output.buffer.length) {
+ output({{{ charCode('\n') }}});
+ }
+ },
+ read: function(stream, buffer, offset, length, pos /* ignored */) {
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ var result;
+ try {
+ result = input();
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ if (result === undefined && bytesRead === 0) {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ if (result === null || result === undefined) break;
+ bytesRead++;
+ buffer[offset+i] = result;
+ }
+ if (bytesRead) {
+ stream.node.timestamp = Date.now();
+ }
+ return bytesRead;
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ for (var i = 0; i < length; i++) {
+ try {
+ output(buffer[offset+i]);
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ }
+ if (length) {
+ stream.node.timestamp = Date.now();
+ }
+ return i;
+ }
+ });
+ return FS.mkdev(path, mode, dev);
+ },
+ createLink: function(parent, name, target, canRead, canWrite) {
+ var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name);
+ return FS.symlink(target, path);
+ },
+ // Makes sure a file's contents are loaded. Returns whether the file has
+ // been loaded successfully. No-op for files that have been loaded already.
+ forceLoadFile: function(obj) {
+ if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;
+ var success = true;
+ if (typeof XMLHttpRequest !== 'undefined') {
+ throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
+ } else if (Module['read']) {
+ // Command-line.
+ try {
+ // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as
+ // read() will try to parse UTF8.
+ obj.contents = intArrayFromString(Module['read'](obj.url), true);
+ } catch (e) {
+ success = false;
+ }
+ } else {
+ throw new Error('Cannot load without read() or XMLHttpRequest.');
+ }
+ if (!success) ___setErrNo(ERRNO_CODES.EIO);
+ return success;
+ },
+ // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous
+ // XHR, which is not possible in browsers except in a web worker! Use preloading,
+ // either --preload-file in emcc or FS.createPreloadedFile
+ createLazyFile: function(parent, name, url, canRead, canWrite) {
+ if (typeof XMLHttpRequest !== 'undefined') {
+ if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
+ // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
+ var LazyUint8Array = function() {
+ this.lengthKnown = false;
+ this.chunks = []; // Loaded chunks. Index is the chunk number
+ }
+ LazyUint8Array.prototype.get = function(idx) {
+ if (idx > this.length-1 || idx < 0) {
+ return undefined;
+ }
+ var chunkOffset = idx % this.chunkSize;
+ var chunkNum = Math.floor(idx / this.chunkSize);
+ return this.getter(chunkNum)[chunkOffset];
+ }
+ LazyUint8Array.prototype.setDataGetter = function(getter) {
+ this.getter = getter;
+ }
+ LazyUint8Array.prototype.cacheLength = function() {
+ // Find length
+ var xhr = new XMLHttpRequest();
+ xhr.open('HEAD', url, false);
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ var datalength = Number(xhr.getResponseHeader("Content-length"));
+ var header;
+ var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
+#if SMALL_XHR_CHUNKS
+ var chunkSize = 1024; // Chunk size in bytes
+#else
+ var chunkSize = 1024*1024; // Chunk size in bytes
+#endif
+
+ if (!hasByteServing) chunkSize = datalength;
+
+ // Function to get a range from the remote URL.
+ var doXHR = (function(from, to) {
+ if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
+ if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
+
+ // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, false);
+ if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
+
+ // Some hints to the browser that we want binary data.
+ if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
+ if (xhr.overrideMimeType) {
+ xhr.overrideMimeType('text/plain; charset=x-user-defined');
+ }
+
+ xhr.send(null);
+ if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
+ if (xhr.response !== undefined) {
+ return new Uint8Array(xhr.response || []);
+ } else {
+ return intArrayFromString(xhr.responseText || '', true);
+ }
+ });
+ var lazyArray = this;
+ lazyArray.setDataGetter(function(chunkNum) {
+ var start = chunkNum * chunkSize;
+ var end = (chunkNum+1) * chunkSize - 1; // including this byte
+ end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
+ lazyArray.chunks[chunkNum] = doXHR(start, end);
+ }
+ if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
+ return lazyArray.chunks[chunkNum];
+ });
+
+ this._length = datalength;
+ this._chunkSize = chunkSize;
+ this.lengthKnown = true;
+ }
+
+ var lazyArray = new LazyUint8Array();
+ Object.defineProperty(lazyArray, "length", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._length;
+ }
+ });
+ Object.defineProperty(lazyArray, "chunkSize", {
+ get: function() {
+ if(!this.lengthKnown) {
+ this.cacheLength();
+ }
+ return this._chunkSize;
+ }
+ });
+
+ var properties = { isDevice: false, contents: lazyArray };
+ } else {
+ var properties = { isDevice: false, url: url };
+ }
+
+ var node = FS.createFile(parent, name, properties, canRead, canWrite);
+ // This is a total hack, but I want to get this lazy file code out of the
+ // core of MEMFS. If we want to keep this lazy file concept I feel it should
+ // be its own thin LAZYFS proxying calls to MEMFS.
+ if (properties.contents) {
+ node.contents = properties.contents;
+ } else if (properties.url) {
+ node.contents = null;
+ node.url = properties.url;
+ }
+ // override each stream op with one that tries to force load the lazy file first
+ var stream_ops = {};
+ var keys = Object.keys(node.stream_ops);
+ keys.forEach(function(key) {
+ var fn = node.stream_ops[key];
+ stream_ops[key] = function() {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ return fn.apply(null, arguments);
+ };
+ });
+ // use a custom read function
+ stream_ops.read = function(stream, buffer, offset, length, position) {
+ if (!FS.forceLoadFile(node)) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ var contents = stream.node.contents;
+ var size = Math.min(contents.length - position, length);
+ if (contents.slice) { // normal array
+ for (var i = 0; i < size; i++) {
+ buffer[offset + i] = contents[position + i];
+ }
+ } else {
+ for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR
+ buffer[offset + i] = contents.get(position + i);
+ }
+ }
+ return size;
+ };
+ node.stream_ops = stream_ops;
+ return node;
+ },
+ // Preloads a file asynchronously. You can call this before run, for example in
+ // preRun. run will be delayed until this file arrives and is set up.
+ // If you call it after run(), you may want to pause the main loop until it
+ // completes, if so, you can use the onload parameter to be notified when
+ // that happens.
+ // In addition to normally creating the file, we also asynchronously preload
+ // the browser-friendly versions of it: For an image, we preload an Image
+ // element and for an audio, and Audio. These are necessary for SDL_Image
+ // and _Mixer to find the files in preloadedImages/Audios.
+ // You can also call this with a typed array instead of a url. It will then
+ // do preloading for the Image/Audio part, as if the typed array were the
+ // result of an XHR that you did manually.
+ createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn) {
+ Browser.init();
+ // TODO we should allow people to just pass in a complete filename instead
+ // of parent and name being that we just join them anyways
+ var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent;
+ function processData(byteArray) {
+ function finish(byteArray) {
+ if (!dontCreateFile) {
+ FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);
+ }
+ if (onload) onload();
+ removeRunDependency('cp ' + fullname);
+ }
+ var handled = false;
+ Module['preloadPlugins'].forEach(function(plugin) {
+ if (handled) return;
+ if (plugin['canHandle'](fullname)) {
+ plugin['handle'](byteArray, fullname, finish, function() {
+ if (onerror) onerror();
+ removeRunDependency('cp ' + fullname);
+ });
+ handled = true;
+ }
+ });
+ if (!handled) finish(byteArray);
+ }
+ addRunDependency('cp ' + fullname);
+ if (typeof url == 'string') {
+ Browser.asyncLoad(url, function(byteArray) {
+ processData(byteArray);
+ }, onerror);
+ } else {
+ processData(url);
+ }
+ },
+
+ //
+ // persistence
+ //
+ indexedDB: function() {
+ return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+ },
+
+ DB_NAME: function() {
+ return 'EM_FS_' + window.location.pathname;
+ },
+ DB_VERSION: 20,
+ DB_STORE_NAME: 'FILE_DATA',
+
+ // asynchronously saves a list of files to an IndexedDB. The DB will be created if not already existing.
+ saveFilesToDB: function(paths, onload, onerror) {
+ onload = onload || function(){};
+ onerror = onerror || function(){};
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = function() {
+ console.log('creating db');
+ var db = openRequest.result;
+ db.createObjectStore(FS.DB_STORE_NAME);
+ };
+ openRequest.onsuccess = function() {
+ var db = openRequest.result;
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function(path) {
+ var putRequest = files.put(FS.analyzePath(path).object.contents, path);
+ putRequest.onsuccess = function() { ok++; if (ok + fail == total) finish() };
+ putRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
+ },
+
+ // asychronously loads a file from IndexedDB.
+ loadFilesFromDB: function(paths, onload, onerror) {
+ onload = onload || function(){};
+ onerror = onerror || function(){};
+ var indexedDB = FS.indexedDB();
+ try {
+ var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
+ } catch (e) {
+ return onerror(e);
+ }
+ openRequest.onupgradeneeded = onerror; // no database to load from
+ openRequest.onsuccess = function() {
+ var db = openRequest.result;
+ try {
+ var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
+ } catch(e) {
+ onerror(e);
+ return;
+ }
+ var files = transaction.objectStore(FS.DB_STORE_NAME);
+ var ok = 0, fail = 0, total = paths.length;
+ function finish() {
+ if (fail == 0) onload(); else onerror();
+ }
+ paths.forEach(function(path) {
+ var getRequest = files.get(path);
+ getRequest.onsuccess = function() {
+ if (FS.analyzePath(path).exists) {
+ FS.unlink(path);
+ }
+ FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
+ ok++;
+ if (ok + fail == total) finish();
+ };
+ getRequest.onerror = function() { fail++; if (ok + fail == total) finish() };
+ });
+ transaction.onerror = onerror;
+ };
+ openRequest.onerror = onerror;
}
}
});
diff --git a/src/library_gl.js b/src/library_gl.js
index c134ad97..16ea5531 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -657,6 +657,20 @@ var LibraryGL = {
glBufferData__sig: 'viiii',
glBufferData: function(target, size, data, usage) {
+ switch (usage) { // fix usages, WebGL only has *_DRAW
+ case 0x88E1: // GL_STREAM_READ
+ case 0x88E2: // GL_STREAM_COPY
+ usage = 0x88E0; // GL_STREAM_DRAW
+ break;
+ case 0x88E5: // GL_STATIC_READ
+ case 0x88E6: // GL_STATIC_COPY
+ usage = 0x88E4; // GL_STATIC_DRAW
+ break;
+ case 0x88E9: // GL_DYNAMIC_READ
+ case 0x88EA: // GL_DYNAMIC_COPY
+ usage = 0x88E8; // GL_DYNAMIC_DRAW
+ break;
+ }
Module.ctx.bufferData(target, HEAPU8.subarray(data, data+size), usage);
},
@@ -3398,6 +3412,7 @@ var LibraryGL = {
// does not work for glBegin/End, where we generate renderer components dynamically and then
// disable them ourselves, but it does help with glDrawElements/Arrays.
if (!this.modifiedClientAttributes) {
+ GL.immediate.vertexCounter = (GL.immediate.stride * count) / 4; // XXX assuming float
return;
}
this.modifiedClientAttributes = false;
diff --git a/src/library_openal.js b/src/library_openal.js
index dea986f3..e8a2e223 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -8,12 +8,6 @@ var LibraryOpenAL = {
QUEUE_INTERVAL: 25,
QUEUE_LOOKAHEAD: 100,
- getCurrentTime: function(context) {
- // currentTime is frozen during execution, use performance timers to
- // emulate the current time at call time
- return (window['performance']['now']() - context.startTime) / 1000.0;
- },
-
updateSources: function(context) {
for (var i = 0; i < context.src.length; i++) {
AL.updateSource(context.src[i]);
@@ -28,7 +22,7 @@ var LibraryOpenAL = {
return;
}
- var currentTime = AL.getCurrentTime(AL.currentContext);
+ var currentTime = AL.currentContext.ctx.currentTime;
var startTime = src.bufferPosition;
for (var i = src.buffersPlayed; i < src.queue.length; i++) {
@@ -79,7 +73,7 @@ var LibraryOpenAL = {
if (src.state !== 0x1013 /* AL_PAUSED */) {
src.state = 0x1012 /* AL_PLAYING */;
// Reset our position.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext);
+ src.bufferPosition = AL.currentContext.ctx.currentTime;
src.buffersPlayed = 0;
#if OPENAL_DEBUG
console.log('setSourceState resetting and playing source ' + idx);
@@ -87,7 +81,7 @@ var LibraryOpenAL = {
} else {
src.state = 0x1012 /* AL_PLAYING */;
// Use the current offset from src.bufferPosition to resume at the correct point.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition;
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
#if OPENAL_DEBUG
console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
#endif
@@ -98,7 +92,7 @@ var LibraryOpenAL = {
if (src.state === 0x1012 /* AL_PLAYING */) {
src.state = 0x1013 /* AL_PAUSED */;
// Store off the current offset to restore with on resume.
- src.bufferPosition = AL.getCurrentTime(AL.currentContext) - src.bufferPosition;
+ src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition;
AL.stopSourceQueue(src);
#if OPENAL_DEBUG
console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4));
@@ -212,8 +206,7 @@ var LibraryOpenAL = {
err: 0,
src: [],
buf: [],
- interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL),
- startTime: window['performance']['now']()
+ interval: setInterval(function() { AL.updateSources(context); }, AL.QUEUE_INTERVAL)
};
AL.contexts.push(context);
return AL.contexts.length;
diff --git a/src/library_sdl.js b/src/library_sdl.js
index d6cb6d18..7c0593b2 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -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;
@@ -2191,11 +2198,37 @@ var LibrarySDL = {
// Joysticks
- SDL_NumJoysticks: function() { return 0 },
+ SDL_NumJoysticks: function() { return 0; },
+
+ SDL_JoystickName: function(deviceIndex) { return 0; },
+
+ SDL_JoystickOpen: function(deviceIndex) { return 0; },
+
+ SDL_JoystickOpened: function(deviceIndex) { return 0; },
+
+ SDL_JoystickIndex: function(joystick) { return 0; },
+
+ SDL_JoystickNumAxes: function(joystick) { return 0; },
+
+ SDL_JoystickNumBalls: function(joystick) { return 0; },
+
+ SDL_JoystickNumHats: function(joystick) { return 0; },
+
+ SDL_JoystickNumButtons: function(joystick) { return 0; },
+
+ SDL_JoystickUpdate: function() {},
+
+ SDL_JoystickEventState: function(state) { return 0; },
+
+ SDL_JoystickGetAxis: function(joystick, axis) { return 0; },
+
+ SDL_JoystickGetHat: function(joystick, hat) { return 0; },
+
+ SDL_JoystickGetBall: function(joystick, ball, dxptr, dyptr) { return -1; },
- SDL_JoystickOpen: function(deviceIndex) { return 0 },
+ SDL_JoystickGetButton: function(joystick, button) { return 0; },
- SDL_JoystickGetButton: function(joystick, button) { return 0 },
+ SDL_JoystickClose: function(joystick) {},
// Misc
@@ -2250,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/library_tty.js b/src/library_tty.js
index 53239989..bf4a2472 100644
--- a/src/library_tty.js
+++ b/src/library_tty.js
@@ -6,23 +6,25 @@ mergeInto(LibraryManager.library, {
$TTY: {
ttys: [],
init: function () {
- if (ENVIRONMENT_IS_NODE) {
- // currently, FS.init does not distinguish if process.stdin is a file or TTY
- // device, it always assumes it's a TTY device. because of this, we're forcing
- // process.stdin to UTF8 encoding to at least make stdin reading compatible
- // with text files until FS.init can be refactored.
- process['stdin']['setEncoding']('utf8');
- }
+ // https://github.com/kripken/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // currently, FS.init does not distinguish if process.stdin is a file or TTY
+ // // device, it always assumes it's a TTY device. because of this, we're forcing
+ // // process.stdin to UTF8 encoding to at least make stdin reading compatible
+ // // with text files until FS.init can be refactored.
+ // process['stdin']['setEncoding']('utf8');
+ // }
},
shutdown: function() {
- if (ENVIRONMENT_IS_NODE) {
- // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)?
- // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation
- // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists?
- // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle
- // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call
- process['stdin']['pause']();
- }
+ // https://github.com/kripken/emscripten/pull/1555
+ // if (ENVIRONMENT_IS_NODE) {
+ // // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)?
+ // // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation
+ // // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists?
+ // // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle
+ // // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call
+ // process['stdin']['pause']();
+ // }
},
register: function(dev, ops) {
TTY.ttys[dev] = { input: [], output: [], ops: ops };
diff --git a/src/modules.js b/src/modules.js
index fa6c0983..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)
@@ -443,6 +445,7 @@ var LibraryManager = {
},
isStubFunction: function(ident) {
+ if (SIDE_MODULE == 1) return false; // cannot eliminate these, as may be implement in the main module and imported by us
var libCall = LibraryManager.library[ident.substr(1)];
return typeof libCall === 'function' && libCall.toString().replace(/\s/g, '') === 'function(){}'
&& !(ident in Functions.implementedFunctions);
@@ -466,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/parseTools.js b/src/parseTools.js
index 66354dca..90c5acab 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -35,8 +35,13 @@ function preprocess(text) {
var op = parts[2];
var value = parts[3];
if (op) {
- assert(op === '==')
- showStack.push(ident in this && this[ident] == value);
+ if (op === '==') {
+ showStack.push(ident in this && this[ident] == value);
+ } else if (op === '!=') {
+ showStack.push(!(ident in this && this[ident] == value));
+ } else {
+ error('unsupported preprecessor op ' + op);
+ }
} else {
showStack.push(ident in this && this[ident] > 0);
}
diff --git a/src/postamble.js b/src/postamble.js
index 897bb269..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;
}
}
diff --git a/src/preamble.js b/src/preamble.js
index 4cae05a6..227b3043 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -595,9 +595,9 @@ var DYNAMIC_BASE = 0, DYNAMICTOP = 0; // dynamic area handled by sbrk
function enlargeMemory() {
#if ALLOW_MEMORY_GROWTH == 0
#if ASM_JS == 0
- abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
+ abort('Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.');
#else
- abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value, or (2) set Module.TOTAL_MEMORY before the program runs.');
+ abort('Cannot enlarge memory arrays in asm.js. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value ' + TOTAL_MEMORY + ', or (2) set Module.TOTAL_MEMORY before the program runs.');
#endif
#else
// TOTAL_MEMORY is the current size of the actual array, and DYNAMICTOP is the new top.
diff --git a/src/proxyClient.js b/src/proxyClient.js
new file mode 100644
index 00000000..41f6a501
--- /dev/null
+++ b/src/proxyClient.js
@@ -0,0 +1,71 @@
+
+// proxy to/from worker
+
+Module.ctx = Module.canvas.getContext('2d');
+
+var worker = new Worker('{{{ filename }}}.js');
+
+worker.onmessage = function(event) {
+ var data = event.data;
+ switch (data.target) {
+ case 'stdout': {
+ Module.print(data.content);
+ break;
+ }
+ case 'stderr': {
+ Module.printErr(data.content);
+ break;
+ }
+ case 'window': {
+ window[data.method]();
+ break;
+ }
+ case 'canvas': {
+ switch (data.op) {
+ case 'resize': {
+ Module.canvas.width = data.width;
+ Module.canvas.height = data.height;
+ Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height);
+ break;
+ }
+ case 'render': {
+ Module.canvasData.data.set(data.image.data);
+ Module.ctx.putImageData(Module.canvasData, 0, 0);
+ break;
+ }
+ default: throw 'eh?';
+ }
+ break;
+ }
+ default: throw 'what?';
+ }
+};
+
+function cloneEvent(event) {
+ var ret = {};
+ for (var x in event) {
+ if (x == x.toUpperCase()) continue;
+ var prop = event[x];
+ if (typeof prop === 'number' || typeof prop === 'string') ret[x] = prop;
+ }
+ return ret;
+};
+
+['keydown', 'keyup', 'keypress', 'blur', 'visibilitychange'].forEach(function(event) {
+ document.addEventListener(event, function(event) {
+ worker.postMessage({ target: 'document', event: cloneEvent(event) });
+ });
+});
+
+['unload'].forEach(function(event) {
+ window.addEventListener(event, function(event) {
+ worker.postMessage({ target: 'window', event: cloneEvent(event) });
+ });
+});
+
+['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
+ Module.canvas.addEventListener(event, function(event) {
+ worker.postMessage({ target: 'canvas', event: cloneEvent(event) });
+ }, true);
+});
+
diff --git a/src/proxyWorker.js b/src/proxyWorker.js
new file mode 100644
index 00000000..8d641b0d
--- /dev/null
+++ b/src/proxyWorker.js
@@ -0,0 +1,128 @@
+
+function EventListener() {
+ this.listeners = {};
+
+ this.addEventListener = function(event, func) {
+ if (!this.listeners[event]) this.listeners[event] = [];
+ this.listeners[event].push(func);
+ };
+
+ this.fireEvent = function(event) {
+ event.preventDefault = function(){};
+
+ if (event.type in this.listeners) {
+ this.listeners[event.type].forEach(function(listener) {
+ listener(event);
+ });
+ }
+ };
+};
+
+var window = this;
+var windowExtra = new EventListener();
+for (var x in windowExtra) window[x] = windowExtra[x];
+
+window.close = function() {
+ postMessage({ target: 'window', method: 'close' });
+};
+
+var document = new EventListener();
+
+document.createElement = function(what) {
+ switch(what) {
+ case 'canvas': {
+ var canvas = new EventListener();
+ canvas.ensureData = function() {
+ if (!canvas.data || canvas.data.width !== canvas.width || canvas.data.height !== canvas.height) {
+ canvas.data = {
+ width: canvas.width,
+ height: canvas.height,
+ data: new Uint8Array(canvas.width*canvas.height*4)
+ };
+ postMessage({ target: 'canvas', op: 'resize', width: canvas.width, height: canvas.height });
+ }
+ };
+ canvas.getContext = function(type) {
+ assert(type == '2d');
+ return {
+ getImageData: function(x, y, w, h) {
+ assert(x == 0 && y == 0 && w == canvas.width && h == canvas.height);
+ canvas.ensureData();
+ return {
+ width: canvas.data.width,
+ height: canvas.data.height,
+ data: new Uint8Array(canvas.data.data) // TODO: can we avoid this copy?
+ };
+ },
+ putImageData: function(image, x, y) {
+ canvas.ensureData();
+ assert(x == 0 && y == 0 && image.width == canvas.width && image.height == canvas.height);
+ canvas.data.data.set(image.data); // TODO: can we avoid this copy?
+ postMessage({ target: 'canvas', op: 'render', image: canvas.data });
+ }
+ };
+ };
+ return canvas;
+ }
+ default: throw 'document.createElement ' + what;
+ }
+};
+
+var console = {
+ log: function(x) {
+ Module.printErr(x);
+ }
+};
+
+Module.canvas = document.createElement('canvas');
+
+Module.setStatus = function(){};
+
+Module.print = function(x) {
+ postMessage({ target: 'stdout', content: x });
+};
+Module.printErr = function(x) {
+ postMessage({ target: 'stderr', content: x });
+};
+
+// buffer messages until the program starts to run
+
+var messageBuffer = null;
+
+function messageResender() {
+ if (calledMain) {
+ assert(messageBuffer && messageBuffer.length > 0);
+ messageBuffer.forEach(function(message) {
+ onmessage(message);
+ });
+ messageBuffer = null;
+ } else {
+ setTimeout(messageResender, 100);
+ }
+}
+
+onmessage = function(message) {
+ if (!calledMain) {
+ if (!messageBuffer) {
+ messageBuffer = [];
+ setTimeout(messageResender, 100);
+ }
+ messageBuffer.push(message);
+ }
+ switch (message.data.target) {
+ case 'document': {
+ document.fireEvent(message.data.event);
+ break;
+ }
+ case 'window': {
+ window.fireEvent(message.data.event);
+ break;
+ }
+ case 'canvas': {
+ Module.canvas.fireEvent(message.data.event);
+ break;
+ }
+ default: throw 'wha? ' + message.data.target;
+ }
+};
+
diff --git a/src/settings.js b/src/settings.js
index b40330b8..1ef0cd58 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -343,6 +343,9 @@ var RUNTIME_LINKED_LIBS = []; // If this is a main file (BUILD_AS_SHARED_LIB ==
// linked libraries can break things.
var BUILD_AS_WORKER = 0; // If set to 1, this is a worker library, a special kind of library
// that is run in a worker. See emscripten.h
+var PROXY_TO_WORKER = 0; // If set to 1, we build the project into a js file that will run
+ // in a worker, and generate an html file that proxies input and
+ // output to/from it.
var LINKABLE = 0; // If set to 1, this file can be linked with others, either as a shared
// library or as the main file that calls a shared library. To enable that,
// we will not internalize all symbols and cull the unused ones, in other
@@ -354,9 +357,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as
// LINKABLE of 0 is very useful in that we can reduce the size of the
// generated code very significantly, by removing everything not actually used.
-var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the
- // module's functions and globals. Implies LINKABLE=1, because we do not want
- // dead code elimination.
+var DLOPEN_SUPPORT = 0; // Full support for dlopen. This is necessary for asm.js and for all code
+ // modes for dlopen(NULL, ...). Note that you must use EMSCRIPTEN_KEEPALIVE
+ // to ensure that functions and globals can be accessed through dlsym,
+ // otherwise LLVM may optimize them out.
var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This
// increases the size of the generated script, but allows you
diff --git a/src/shell_sharedlib.js b/src/shell_sharedlib.js
index 1d34c73e..c070c617 100644
--- a/src/shell_sharedlib.js
+++ b/src/shell_sharedlib.js
@@ -1,8 +1,24 @@
// Capture the output of this into a variable, if you want
-(function(FUNCTION_TABLE_OFFSET, globalScope) {
+(function(FUNCTION_TABLE_OFFSET, parentModule) {
var Module = {};
var args = [];
Module.arguments = [];
+ Module.print = parentModule.print;
+ Module.printErr = parentModule.printErr;
+
+ Module.cleanups = [];
+
+#if ASM_JS
+ var H_BASE = 0;
+ // Each module has its own stack
+ var STACKTOP = parentModule['_malloc'](TOTAL_STACK);
+ assert(STACKTOP % 8 == 0);
+ var STACK_MAX = STACKTOP + TOTAL_STACK;
+ Module.cleanups.push(function() {
+ parentModule['_free'](STACKTOP); // XXX ensure exported
+ parentModule['_free'](H_BASE);
+ });
+#endif
{{BODY}}
@@ -10,3 +26,4 @@
return Module;
});
+
diff --git a/system/bin/sdl-config b/system/bin/sdl-config
new file mode 100755
index 00000000..51dff31d
--- /dev/null
+++ b/system/bin/sdl-config
@@ -0,0 +1,13 @@
+#!/usr/bin/env python2
+
+import sys
+
+print >> sys.stderr, 'emscripten sdl-config called with', sys.argv
+
+args = sys.argv[1:]
+
+if args[0] == '--cflags':
+ print ''
+elif '--version' in args:
+ print '1.3.0'
+
diff --git a/system/include/libcxx/__locale b/system/include/libcxx/__locale
index 93147ec0..5ae8fa59 100644
--- a/system/include/libcxx/__locale
+++ b/system/include/libcxx/__locale
@@ -21,9 +21,9 @@
#include <locale.h>
#ifdef _LIBCPP_MSVCRT
# include <support/win32/locale_win32.h>
-#elif (defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun__)) || defined(EMSCRIPTEN)
+#elif (defined(__GLIBC__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun__)) || defined(__EMSCRIPTEN__)
# include <xlocale.h>
-#endif // _WIN32 || __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun__ || EMSCRIPTEN
+#endif // _WIN32 || __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun__ || __EMSCRIPTEN__
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
@@ -339,12 +339,12 @@ public:
static const mask punct = _PUNCT;
static const mask xdigit = _HEX;
static const mask blank = _BLANK;
-#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
#ifdef __APPLE__
typedef __uint32_t mask;
#elif defined(__FreeBSD__)
typedef unsigned long mask;
-#elif defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__EMSCRIPTEN__) || defined(__NetBSD__)
typedef unsigned short mask;
#endif
static const mask space = _CTYPE_S;
@@ -373,7 +373,7 @@ public:
static const mask punct = _ISPUNCT;
static const mask xdigit = _ISXDIGIT;
static const mask blank = _ISBLANK;
-#else // __GLIBC__ || _WIN32 || __APPLE__ || __FreeBSD__ || EMSCRIPTEN || __sun__
+#else // __GLIBC__ || _WIN32 || __APPLE__ || __FreeBSD__ || __EMSCRIPTEN__ || __sun__
typedef unsigned long mask;
static const mask space = 1<<0;
static const mask print = 1<<1;
@@ -596,7 +596,7 @@ public:
#endif
_LIBCPP_ALWAYS_INLINE const mask* table() const _NOEXCEPT {return __tab_;}
static const mask* classic_table() _NOEXCEPT;
-#if defined(__GLIBC__) || defined(EMSCRIPTEN)
+#if defined(__GLIBC__) || defined(__EMSCRIPTEN__)
static const int* __classic_upper_table() _NOEXCEPT;
static const int* __classic_lower_table() _NOEXCEPT;
#endif
diff --git a/system/include/libcxx/locale b/system/include/libcxx/locale
index 00a275f9..f5f5fff9 100644
--- a/system/include/libcxx/locale
+++ b/system/include/libcxx/locale
@@ -224,7 +224,7 @@ typedef _VSTD::unique_ptr<__locale_struct, decltype(&uselocale)> __locale_raii;
// OSX has nice foo_l() functions that let you turn off use of the global
// locale. Linux, not so much. The following functions avoid the locale when
// that's possible and otherwise do the wrong thing. FIXME.
-#if defined(__linux__) || defined(EMSCRIPTEN)
+#if defined(__linux__) || defined(__EMSCRIPTEN__)
#ifdef _LIBCPP_LOCALE__L_EXTENSIONS
decltype(MB_CUR_MAX_L(_VSTD::declval<locale_t>()))
diff --git a/system/lib/libcxx/exception.cpp b/system/lib/libcxx/exception.cpp
index d3e1b292..3487bd8b 100644
--- a/system/lib/libcxx/exception.cpp
+++ b/system/lib/libcxx/exception.cpp
@@ -78,7 +78,7 @@ get_terminate() _NOEXCEPT
return __sync_fetch_and_add(&__terminate_handler, (terminate_handler)0);
}
-#ifndef EMSCRIPTEN // We provide this in JS
+#ifndef __EMSCRIPTEN__ // We provide this in JS
_LIBCPP_NORETURN
void
terminate() _NOEXCEPT
@@ -101,10 +101,10 @@ terminate() _NOEXCEPT
}
#endif // _LIBCPP_NO_EXCEPTIONS
}
-#endif // !EMSCRIPTEN
+#endif // !__EMSCRIPTEN__
#endif // !defined(LIBCXXRT) && !defined(_LIBCPPABI_VERSION)
-#if !defined(LIBCXXRT) && !defined(__GLIBCXX__) && !defined(EMSCRIPTEN)
+#if !defined(LIBCXXRT) && !defined(__GLIBCXX__) && !defined(__EMSCRIPTEN__)
bool uncaught_exception() _NOEXCEPT
{
#if defined(__APPLE__) || defined(_LIBCPPABI_VERSION)
diff --git a/system/lib/libcxx/locale.cpp b/system/lib/libcxx/locale.cpp
index d95d0c9c..ad64668f 100644
--- a/system/lib/libcxx/locale.cpp
+++ b/system/lib/libcxx/locale.cpp
@@ -792,7 +792,7 @@ ctype<wchar_t>::do_toupper(char_type c) const
{
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
return isascii(c) ? _DefaultRuneLocale.__mapupper[c] : c;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ? ctype<char>::__classic_upper_table()[c] : c;
#else
return (isascii(c) && iswlower_l(c, __cloc())) ? c-L'a'+L'A' : c;
@@ -805,7 +805,7 @@ ctype<wchar_t>::do_toupper(char_type* low, const char_type* high) const
for (; low != high; ++low)
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
*low = isascii(*low) ? _DefaultRuneLocale.__mapupper[*low] : *low;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
*low = isascii(*low) ? ctype<char>::__classic_upper_table()[*low]
: *low;
#else
@@ -819,7 +819,7 @@ ctype<wchar_t>::do_tolower(char_type c) const
{
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
return isascii(c) ? _DefaultRuneLocale.__maplower[c] : c;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ? ctype<char>::__classic_lower_table()[c] : c;
#else
return (isascii(c) && isupper_l(c, __cloc())) ? c-L'A'+'a' : c;
@@ -832,7 +832,7 @@ ctype<wchar_t>::do_tolower(char_type* low, const char_type* high) const
for (; low != high; ++low)
#ifdef _LIBCPP_HAS_DEFAULTRUNELOCALE
*low = isascii(*low) ? _DefaultRuneLocale.__maplower[*low] : *low;
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
*low = isascii(*low) ? ctype<char>::__classic_lower_table()[*low]
: *low;
#else
@@ -901,7 +901,7 @@ ctype<char>::do_toupper(char_type c) const
static_cast<char>(_DefaultRuneLocale.__mapupper[static_cast<ptrdiff_t>(c)]) : c;
#elif defined(__NetBSD__)
return static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(c)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
return isascii(c) ?
static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(c)]) : c;
#else
@@ -918,7 +918,7 @@ ctype<char>::do_toupper(char_type* low, const char_type* high) const
static_cast<char>(_DefaultRuneLocale.__mapupper[static_cast<ptrdiff_t>(*low)]) : *low;
#elif defined(__NetBSD__)
*low = static_cast<char>(__classic_upper_table()[static_cast<unsigned char>(*low)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
*low = isascii(*low) ?
static_cast<char>(__classic_upper_table()[static_cast<size_t>(*low)]) : *low;
#else
@@ -935,7 +935,7 @@ ctype<char>::do_tolower(char_type c) const
static_cast<char>(_DefaultRuneLocale.__maplower[static_cast<ptrdiff_t>(c)]) : c;
#elif defined(__NetBSD__)
return static_cast<char>(__classic_lower_table()[static_cast<unsigned char>(c)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN) || defined(__NetBSD__)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__NetBSD__)
return isascii(c) ?
static_cast<char>(__classic_lower_table()[static_cast<size_t>(c)]) : c;
#else
@@ -951,7 +951,7 @@ ctype<char>::do_tolower(char_type* low, const char_type* high) const
*low = isascii(*low) ? static_cast<char>(_DefaultRuneLocale.__maplower[static_cast<ptrdiff_t>(*low)]) : *low;
#elif defined(__NetBSD__)
*low = static_cast<char>(__classic_lower_table()[static_cast<unsigned char>(*low)]);
-#elif defined(__GLIBC__) || defined(EMSCRIPTEN)
+#elif defined(__GLIBC__) || defined(__EMSCRIPTEN__)
*low = isascii(*low) ? static_cast<char>(__classic_lower_table()[static_cast<size_t>(*low)]) : *low;
#else
*low = (isascii(*low) && isupper_l(*low, __cloc())) ? *low-'A'+'a' : *low;
@@ -992,7 +992,7 @@ ctype<char>::do_narrow(const char_type* low, const char_type* high, char dfault,
return low;
}
-#ifdef EMSCRIPTEN
+#ifdef __EMSCRIPTEN__
extern "C" const unsigned short ** __ctype_b_loc();
extern "C" const int ** __ctype_tolower_loc();
extern "C" const int ** __ctype_toupper_loc();
@@ -1013,7 +1013,7 @@ ctype<char>::classic_table() _NOEXCEPT
return _ctype+1; // internal ctype mask table defined in msvcrt.dll
// This is assumed to be safe, which is a nonsense assumption because we're
// going to end up dereferencing it later...
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
return *__ctype_b_loc();
#else
// Platform not supported: abort so the person doing the port knows what to
@@ -1050,7 +1050,7 @@ ctype<char>::__classic_upper_table() _NOEXCEPT
return _C_toupper_tab_ + 1;
}
-#elif defined(EMSCRIPTEN)
+#elif defined(__EMSCRIPTEN__)
const int*
ctype<char>::__classic_lower_table() _NOEXCEPT
{
@@ -1062,7 +1062,7 @@ ctype<char>::__classic_upper_table() _NOEXCEPT
{
return *__ctype_toupper_loc();
}
-#endif // __GLIBC__ || EMSCRIPTEN || __NETBSD__
+#endif // __GLIBC__ || __EMSCRIPTEN__ || __NETBSD__
// template <> class ctype_byname<char>
diff --git a/system/lib/libcxxabi/src/cxa_new_delete.cpp b/system/lib/libcxxabi/src/cxa_new_delete.cpp
index e6fee5f2..7fd0b3b7 100644
--- a/system/lib/libcxxabi/src/cxa_new_delete.cpp
+++ b/system/lib/libcxxabi/src/cxa_new_delete.cpp
@@ -228,7 +228,7 @@ bad_array_new_length::what() const _NOEXCEPT
return "bad_array_new_length";
}
-#ifdef EMSCRIPTEN
+#ifdef __EMSCRIPTEN__
// We don't build the new.cpp from libcxx, so we need to define this.
void
__throw_bad_alloc()
diff --git a/tests/browser_main.cpp b/tests/browser_main.cpp
new file mode 100644
index 00000000..efdce1be
--- /dev/null
+++ b/tests/browser_main.cpp
@@ -0,0 +1,42 @@
+#include <assert.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <emscripten.h>
+
+typedef void (*voidfunc)();
+typedef int (*intfunc)();
+
+void *lib_handle;
+voidfunc onefunc;
+intfunc twofunc;
+
+void next(const char *x) {
+ lib_handle = dlopen("themodule.js", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ onefunc = (voidfunc)dlsym(lib_handle, "one");
+ twofunc = (intfunc)dlsym(lib_handle, "two");
+ assert(onefunc && twofunc);
+
+ assert(twofunc() == 0);
+ onefunc();
+ assert(twofunc() == 1);
+ onefunc();
+ onefunc();
+ assert(twofunc() == 3);
+ onefunc();
+ onefunc();
+ onefunc();
+ onefunc();
+ assert(twofunc() == 7);
+ onefunc();
+ int result = twofunc();
+ REPORT_RESULT();
+}
+
+int main() {
+ emscripten_async_wget("module.js", "themodule.js", next, NULL);
+
+ return 0;
+}
+
diff --git a/tests/browser_module.cpp b/tests/browser_module.cpp
new file mode 100644
index 00000000..85d724b5
--- /dev/null
+++ b/tests/browser_module.cpp
@@ -0,0 +1,15 @@
+
+int state = 0;
+
+extern "C" {
+
+void one() {
+ state++;
+}
+
+int two() {
+ return state;
+}
+
+}
+
diff --git a/tests/cmake/target_html/CMakeLists.txt b/tests/cmake/target_html/CMakeLists.txt
index 9f891e71..8b0528eb 100644
--- a/tests/cmake/target_html/CMakeLists.txt
+++ b/tests/cmake/target_html/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.8)
-project(hello_world_gles.html)
+project(hello_world_gles)
file(GLOB sourceFiles ../../hello_world_gles.c)
@@ -10,5 +10,7 @@ else() # Either MinSizeRel, RelWithDebInfo or Release, all which run with optimi
SET(linkFlags "-O2")
endif()
-add_executable(hello_world_gles.html ${sourceFiles})
-set_target_properties(hello_world_gles.html PROPERTIES LINK_FLAGS "${linkFlags}")
+SET(CMAKE_EXECUTABLE_SUFFIX ".html")
+
+add_executable(hello_world_gles ${sourceFiles})
+set_target_properties(hello_world_gles PROPERTIES LINK_FLAGS "${linkFlags}")
diff --git a/tests/cmake/target_js/CMakeLists.txt b/tests/cmake/target_js/CMakeLists.txt
index 860b70a9..cee5fc42 100644
--- a/tests/cmake/target_js/CMakeLists.txt
+++ b/tests/cmake/target_js/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 2.8)
-project(hello_world.js)
+project(hello_world)
file(GLOB sourceFiles ../../hello_world.cpp)
@@ -10,5 +10,23 @@ else() # Either MinSizeRel, RelWithDebInfo or Release, all which run with optimi
SET(linkFlags "-O2")
endif()
-add_executable(hello_world.js ${sourceFiles})
-set_target_properties(hello_world.js PROPERTIES LINK_FLAGS "${linkFlags}")
+SET(CMAKE_EXECUTABLE_SUFFIX ".js")
+
+if (WIN32)
+ message(FATAL_ERROR "WIN32 should not be defined when cross-compiling!")
+endif()
+
+if (APPLE)
+ message(FATAL_ERROR "APPLE should not be defined when cross-compiling!")
+endif()
+
+if (NOT EMSCRIPTEN)
+ message(FATAL_ERROR "EMSCRIPTEN should be defined when cross-compiling!")
+endif()
+
+if (NOT CMAKE_C_SIZEOF_DATA_PTR)
+ message(FATAL_ERROR "CMAKE_C_SIZEOF_DATA_PTR was not defined!")
+endif()
+
+add_executable(hello_world ${sourceFiles})
+set_target_properties(hello_world PROPERTIES LINK_FLAGS "${linkFlags}")
diff --git a/tests/dlmalloc_proxy.c b/tests/dlmalloc_proxy.c
new file mode 100644
index 00000000..06137c42
--- /dev/null
+++ b/tests/dlmalloc_proxy.c
@@ -0,0 +1,85 @@
+// Emscripten tests
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+typedef void *(*mallocer)(int n);
+typedef void (*freeer)(void *p);
+
+void *lib_handle;
+int handles = 0;
+mallocer mallocproxy = NULL;
+freeer freeproxy = NULL;
+
+void get_lib() {
+ //printf("get lib\n");
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+ handles++;
+
+ mallocproxy = (mallocer)dlsym(lib_handle, "mallocproxy");
+ assert(mallocproxy!= NULL);
+ freeproxy = (freeer)dlsym(lib_handle, "freeproxy");
+ assert(freeproxy!= NULL);
+}
+
+void unget_lib() {
+ //printf("unget lib\n");
+ assert(lib_handle);
+ dlclose(lib_handle);
+ handles--;
+ if (handles == 0) lib_handle = NULL;
+}
+
+int main() {
+ int n = 0, total = 0, l = 0;
+ void *allocs[50];
+ allocs[10] = malloc(10); // pull in real malloc
+ for (int i = 0; i < 1000; i++) {
+ //printf("%d: total ever %d MB, current MB %d, total libs %d\n", i, total, n, l);
+ if (i % 5 == 0) {
+ if (handles < 10) {
+ get_lib();
+ l++;
+ }
+ }
+ if (i % 7 == 0) {
+ if (handles > 0) unget_lib();
+ }
+ if (i % 3 == 0) {
+ if (handles > 0) {
+ if (n < 10) {
+ if (i % 2 == 0) {
+ //printf("alloc\n");
+ allocs[n++] = mallocproxy(1024*1024);
+ } else {
+ //printf("real alloc\n");
+ allocs[n++] = malloc(1024*1024);
+ }
+ total++;
+ } else {
+ //printf("real free\n");
+ free(allocs[--n]); // real free
+ }
+ }
+ }
+ if (i % 4 == 0) {
+ if (handles > 0 && n > 0) {
+ //printf("free\n");
+ if (i % 2 == 0) {
+ //printf("free\n");
+ freeproxy(allocs[--n]);
+ } else {
+ //printf("real free\n");
+ free(allocs[--n]);
+ }
+ }
+ }
+ }
+ while (n > 0) free(allocs[--n]); // real free
+ while (handles > 0) unget_lib();
+ printf("*%d,%d*\n", total, l);
+}
+
diff --git a/tests/filesystem/src.js b/tests/filesystem/src.js
index dbdd4bed..91337f5b 100644
--- a/tests/filesystem/src.js
+++ b/tests/filesystem/src.js
@@ -1,16 +1,18 @@
var dummy_device = FS.makedev(64, 0);
FS.registerDevice(dummy_device, {});
-FS.createFolder('/', 'forbidden', false, false);
-FS.createFolder('/forbidden', 'test', true, true);
-FS.createPath('/', 'abc/123', true, true);
-FS.createPath('/', 'abc/456', true, true);
-FS.createPath('/', 'def/789', true, true);
-FS.mkdev('/abc/deviceA', 0666, dummy_device);
-FS.mkdev('/def/deviceB', 0666, dummy_device);
-FS.createLink('/abc', 'localLink', '123', true, true);
-FS.createLink('/abc', 'rootLink', '/', true, true);
-FS.createLink('/abc', 'relativeLink', '../def', true, true);
+FS.mkdir('/forbidden', 0000);
+FS.mkdir('/forbidden/test');
+FS.mkdir('/abc');
+FS.mkdir('/abc/123');
+FS.mkdir('/abc/456');
+FS.mkdir('/def');
+FS.mkdir('/def/789');
+FS.mkdev('/abc/deviceA', dummy_device);
+FS.mkdev('/def/deviceB', dummy_device);
+FS.symlink('123', '/abc/localLink');
+FS.symlink('/', '/abc/rootLink');
+FS.symlink('../def', '/abc/relativeLink');
FS.ignorePermissions = false;
function explore(path) {
diff --git a/tests/gl_vertex_buffer.c b/tests/gl_vertex_buffer.c
new file mode 100644
index 00000000..6b695462
--- /dev/null
+++ b/tests/gl_vertex_buffer.c
@@ -0,0 +1,195 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+ THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+ AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+ THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+ THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+ OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+ MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+ RESULTING FROM THE USE, MODIFICATION, OR
+ REDISTRIBUTION OF THIS SOFTWARE.
+ */
+
+#if !EMSCRIPTEN
+#define USE_GLEW 0
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include <SDL/SDL.h>
+
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ typedef struct Color {
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+ } Color;
+
+ typedef struct Vertex {
+ GLfloat x;
+ GLfloat y;
+ Color color;
+ } Vertex;
+
+ Vertex vertices[18] = {
+ {-1.00, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ Vertex vertices2[18] = {
+ {-1.00, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ // make a vertex buffer for the second set of vertices
+ GLuint vbo = 0;
+ glGenBuffers(1, &vbo);
+
+
+ // bind to it
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ // send it to gl
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_READ); // GL_STATIC_READ is not in WebGL!
+
+ // unbind from it
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+
+ // DRAW
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // This test ensures that we can use two separate arrays in memory for different
+ // attributes, and that they each can have different stride.
+ // The first test shows implicit striding (the zero indicates tightly packed)
+ // The second test shows explicit striding where the stride is passed in
+ // even though it also is tightly packed
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ // TEST 1 - clientside data
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vertices);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vertices[0].color);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+
+
+
+ // TEST 2 - bind to array buffer, gl*Pointer calls are offsets into the buffer, which was previously uploaded to
+
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), 0);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (GLvoid*)((GLvoid*)&vertices2[0].color - (GLvoid*)&vertices2[0]));
+
+// gldrawarrays first with a low number of vertices, then with a high number
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/gl_vertex_buffer.png b/tests/gl_vertex_buffer.png
new file mode 100644
index 00000000..3e1f2230
--- /dev/null
+++ b/tests/gl_vertex_buffer.png
Binary files differ
diff --git a/tests/gl_vertex_buffer_pre.c b/tests/gl_vertex_buffer_pre.c
new file mode 100644
index 00000000..84b76569
--- /dev/null
+++ b/tests/gl_vertex_buffer_pre.c
@@ -0,0 +1,177 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+ THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+ AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+ THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+ THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+ OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+ MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+ RESULTING FROM THE USE, MODIFICATION, OR
+ REDISTRIBUTION OF THIS SOFTWARE.
+ */
+
+#if !EMSCRIPTEN
+#define USE_GLEW 0
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include <SDL/SDL.h>
+
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ typedef struct Color {
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+ } Color;
+
+ typedef struct Vertex {
+ GLfloat x;
+ GLfloat y;
+ Color color;
+ } Vertex;
+
+ Vertex vertices[18] = {
+ {-1.00, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 1.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 1.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 1.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ Vertex vertices2[18] = {
+ {-1.00, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ {-0.50, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.25, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ {-0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ {-0.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ {-0.00, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 0.25, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.25, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.50, -1.0, {0xFF, 0x00, 0xFF, 0xFF}},
+ { 0.50, 0.0, {0xFF, 0xFF, 0x00, 0xFF}},
+ { 0.75, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 0.75, 0.0, {0xFF, 0xFF, 0xFF, 0xFF}},
+ { 1.00, -1.0, {0xFF, 0x00, 0x00, 0xFF}},
+ { 1.00, 0.0, {0xFF, 0xFF, 0x00, 0xFF}}
+ };
+
+ // make a vertex buffer for the second set of vertices
+ GLuint vbo = 0;
+ glGenBuffers(1, &vbo);
+
+
+ // bind to it
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ // send it to gl
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
+
+ // unbind from it
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+
+ // DRAW
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // This test ensures that we can use two separate arrays in memory for different
+ // attributes, and that they each can have different stride.
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, sizeof(Vertex), vertices);
+ glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &vertices[0].color);
+ glDrawArrays(GL_TRIANGLE_STRIP, 10, 3);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/gl_vertex_buffer_pre.png b/tests/gl_vertex_buffer_pre.png
new file mode 100644
index 00000000..5677a868
--- /dev/null
+++ b/tests/gl_vertex_buffer_pre.png
Binary files differ
diff --git a/tests/runner.py b/tests/runner.py
index f0e61c4e..ddc97ea4 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -254,7 +254,7 @@ process(sys.argv[1])
os.chdir(cwd)
out = open(stdout, 'r').read()
err = open(stderr, 'r').read()
- if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS:
+ if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS == 1:
err = self.validate_asmjs(err)
if output_nicerizer:
ret = output_nicerizer(out, err)
@@ -646,7 +646,7 @@ class BrowserCore(RunnerCore):
});
''' % basename)
- def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0,
+ def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0, manual_reference=False, post_build=None,
args=[], outfile='test.html', message='.'): # TODO: use in all other tests
# if we are provided the source and not a path, use that
filename_is_src = '\n' in filename
@@ -663,9 +663,11 @@ class BrowserCore(RunnerCore):
expected = [str(i) for i in range(0, reference_slack+1)]
shutil.copyfile(filepath, temp_filepath)
self.reftest(path_from_root('tests', reference))
- args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1']
+ if not manual_reference:
+ args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1']
Popen([PYTHON, EMCC, temp_filepath, '-o', outfile] + args).communicate()
assert os.path.exists(outfile)
+ if post_build: post_build()
if type(expected) is str: expected = [expected]
self.run_browser(outfile, message, ['/report_result?' + e for e in expected])
diff --git a/tests/sdl_canvas_proxy.c b/tests/sdl_canvas_proxy.c
new file mode 100644
index 00000000..27ef3bb4
--- /dev/null
+++ b/tests/sdl_canvas_proxy.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <SDL/SDL.h>
+#include <assert.h>
+#include <emscripten.h>
+
+int main(int argc, char **argv) {
+ FILE *f = fopen("data.txt", "rb");
+ assert(f);
+ assert(fgetc(f) == 'd');
+ assert(fgetc(f) == 'a');
+ assert(fgetc(f) == 't');
+ assert(fgetc(f) == 'u');
+ assert(fgetc(f) == 'm');
+ fclose(f);
+
+ SDL_Init(SDL_INIT_VIDEO);
+ SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+
+ SDL_LockSurface(screen);
+ unsigned int *pixels = (unsigned int *)screen->pixels;
+ for (int x = 0; x < screen->w; x++) {
+ for (int y = 0; y < screen->h; y++) {
+ pixels[x + y*screen->h] = x < 300 ? (y < 200 ? 0x3377AA88 : 0xAA3377CC) : (y < 200 ? 0x0066AA77 : 0xAA006699);
+ }
+ }
+ SDL_UnlockSurface(screen);
+
+ SDL_Quit();
+
+ EM_ASM(window.close());
+ return 0;
+}
+
diff --git a/tests/sdl_canvas_proxy.png b/tests/sdl_canvas_proxy.png
new file mode 100644
index 00000000..cc96acfd
--- /dev/null
+++ b/tests/sdl_canvas_proxy.png
Binary files differ
diff --git a/tests/sdl_key_proxy.c b/tests/sdl_key_proxy.c
new file mode 100644
index 00000000..bc233f29
--- /dev/null
+++ b/tests/sdl_key_proxy.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_ttf.h>
+#include <emscripten.h>
+
+int result = 1;
+
+void one() {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ printf("got event %d\n", event.type);
+ switch(event.type) {
+ case SDL_KEYDOWN:
+ break;
+ case SDL_KEYUP:
+ // don't handle the modifier key events
+ if (event.key.keysym.sym == SDLK_LCTRL ||
+ event.key.keysym.sym == SDLK_LSHIFT ||
+ event.key.keysym.sym == SDLK_LALT) {
+ return;
+ }
+ if ((event.key.keysym.mod & KMOD_LCTRL) || (event.key.keysym.mod & KMOD_RCTRL)) {
+ result *= 2;
+ }
+ if ((event.key.keysym.mod & KMOD_LSHIFT) || (event.key.keysym.mod & KMOD_RSHIFT)) {
+ result *= 3;
+ }
+ if ((event.key.keysym.mod & KMOD_LALT) || (event.key.keysym.mod & KMOD_RALT)) {
+ result *= 5;
+ }
+ switch (event.key.keysym.sym) {
+ case SDLK_RIGHT: printf("right\n"); result *= 7; break;
+ case SDLK_LEFT: printf("left\n"); result *= 11; break;
+ case SDLK_DOWN: printf("down\n"); result *= 13; break;
+ case SDLK_UP: printf("up\n"); result *= 17; break;
+ case SDLK_a: printf("a\n"); result *= 19; break;
+ default: {
+ if (event.key.keysym.scancode == SDL_SCANCODE_B) {
+ printf("b scancode\n"); result *= 23; break;
+ }
+ printf("unknown key: sym %d scancode %d\n", event.key.keysym.sym, event.key.keysym.scancode);
+ REPORT_RESULT();
+ emscripten_run_script("throw 'done'"); // comment this out to leave event handling active. Use the following to log DOM keys:
+ // addEventListener('keyup', function(event) { console.log(event.keyCode) }, true)
+ }
+ }
+ break;
+ default: /* Report an unhandled event */
+ printf("I don't know what this event is!\n");
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ printf("main\n");
+ SDL_Init(SDL_INIT_VIDEO);
+ SDL_Surface *screen = SDL_SetVideoMode(600, 450, 32, SDL_HWSURFACE);
+
+ if (argc == 1337) one(); // keep it alive
+
+ return 0;
+}
+
diff --git a/tests/test_browser.py b/tests/test_browser.py
index a0c4dceb..e6fd6544 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -627,6 +627,28 @@ If manually bisecting:
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html', '-s', 'LEGACY_GL_EMULATION=1']).communicate()
self.run_browser('page.html', '', '/report_result?1')
+ def test_sdl_canvas_proxy(self):
+ def post():
+ html = open('test.html').read()
+ html = html.replace('</body>', '''
+<script>
+function assert(x, y) { if (!x) throw 'assertion failed ' + y }
+
+%s
+
+var windowClose = window.close;
+window.close = function() {
+ doReftest();
+ setTimeout(windowClose, 1000);
+};
+</script>
+</body>''' % open('reftest.js').read())
+ open('test.html', 'w').write(html)
+
+ open('data.txt', 'w').write('datum')
+
+ self.btest('sdl_canvas_proxy.c', reference='sdl_canvas_proxy.png', args=['--proxy-to-worker', '--preload-file', 'data.txt'], manual_reference=True, post_build=post)
+
def test_sdl_key(self):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Module.postRun = function() {
@@ -658,6 +680,52 @@ If manually bisecting:
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate()
self.run_browser('page.html', '', '/report_result?223092870')
+ def test_sdl_key_proxy(self):
+ open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
+ var Module = {};
+ Module.postRun = function() {
+ function doOne() {
+ Module._one();
+ setTimeout(doOne, 1000/60);
+ }
+ setTimeout(doOne, 1000/60);
+ }
+ ''')
+
+ def post():
+ html = open('test.html').read()
+ html = html.replace('</body>', '''
+<script>
+function keydown(c) {
+ var event = document.createEvent("KeyboardEvent");
+ event.initKeyEvent("keydown", true, true, window,
+ 0, 0, 0, 0,
+ c, c);
+ document.dispatchEvent(event);
+}
+
+function keyup(c) {
+ var event = document.createEvent("KeyboardEvent");
+ event.initKeyEvent("keyup", true, true, window,
+ 0, 0, 0, 0,
+ c, c);
+ document.dispatchEvent(event);
+}
+
+keydown(1250);keydown(38);keyup(38);keyup(1250); // alt, up
+keydown(1248);keydown(1249);keydown(40);keyup(40);keyup(1249);keyup(1248); // ctrl, shift, down
+keydown(37);keyup(37); // left
+keydown(39);keyup(39); // right
+keydown(65);keyup(65); // a
+keydown(66);keyup(66); // b
+keydown(100);keyup(100); // trigger the end
+
+</script>
+</body>''')
+ open('test.html', 'w').write(html)
+
+ self.btest('sdl_key_proxy.c', '223092870', args=['--proxy-to-worker', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']'''], manual_reference=True, post_build=post)
+
def test_sdl_text(self):
open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('''
Module.postRun = function() {
@@ -1207,6 +1275,12 @@ If manually bisecting:
def test_gl_stride(self):
self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'])
+ def test_gl_vertex_buffer_pre(self):
+ self.btest('gl_vertex_buffer_pre.c', reference='gl_vertex_buffer_pre.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'])
+
+ def test_gl_vertex_buffer(self):
+ self.btest('gl_vertex_buffer.c', reference='gl_vertex_buffer.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1)
+
def test_matrix_identity(self):
self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'], args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1404,3 +1478,8 @@ If manually bisecting:
def test_emscripten_async_wget2(self):
self.btest('http.cpp', expected='0', args=['-I' + path_from_root('tests')])
+
+ def test_module(self):
+ Popen([PYTHON, EMCC, path_from_root('tests', 'browser_module.cpp'), '-o', 'module.js', '-O2', '-s', 'SIDE_MODULE=1', '-s', 'DLOPEN_SUPPORT=1', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two"]']).communicate()
+ self.btest('browser_main.cpp', args=['-O2', '-s', 'MAIN_MODULE=1', '-s', 'DLOPEN_SUPPORT=1'], expected='8')
+
diff --git a/tests/test_core.py b/tests/test_core.py
index 16bf9a00..5d4f35e8 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -3742,7 +3742,8 @@ def process(filename):
self.do_run(open(path_from_root('tests', 'emscripten_get_now.cpp')).read(), 'Timer resolution is good.')
def test_inlinejs(self):
- if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = r'''
#include <stdio.h>
@@ -3762,7 +3763,8 @@ def process(filename):
self.do_run(src, 'Inline JS is very cool\n3.64\n')
def test_inlinejs2(self):
- if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = r'''
#include <stdio.h>
@@ -3773,8 +3775,8 @@ def process(filename):
}
void mult() {
- asm("var $_$1 = Math.abs(-100); $_$1 *= 2;"); // multiline
- asm __volatile__("Module.print($_$1); Module.print('\n')");
+ asm("var $_$1 = Math.abs(-100); $_$1 *= 2; Module.print($_$1)"); // multiline
+ asm __volatile__("Module.print('done')");
}
int main(int argc, char **argv) {
@@ -3784,7 +3786,7 @@ def process(filename):
}
'''
- self.do_run(src, '4\n200\n')
+ self.do_run(src, '4\n200\ndone\n')
def test_inlinejs3(self):
if Settings.ASM_JS: return self.skip('asm does not support random code, TODO: something that works in asm')
@@ -5580,11 +5582,53 @@ The current type of b is: 9
Settings.RUNTIME_LINKED_LIBS = ['liblib.so'];
self.do_run(main, 'supp: 54,2\nmain: 56\nsupp see: 543\nmain see: 76\nok.')
- def test_dlfcn_basic(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ def can_dlfcn(self):
+ if self.emcc_args and '--memory-init-file' in self.emcc_args:
+ for i in range(len(self.emcc_args)):
+ if self.emcc_args[i] == '--memory-init-file':
+ self.emcc_args = self.emcc_args[:i] + self.emcc_args[i+2:]
+ break
- Settings.NAMED_GLOBALS = 1
+ if Settings.ASM_JS:
+ Settings.DLOPEN_SUPPORT = 1
+ else:
+ Settings.NAMED_GLOBALS = 1
+
+ if not self.is_le32():
+ self.skip('need le32 for dlfcn support')
+ return False
+ else:
+ return True
+
+ def prep_dlfcn_lib(self):
+ if Settings.ASM_JS:
+ Settings.MAIN_MODULE = 0
+ Settings.SIDE_MODULE = 1
+ else:
+ Settings.BUILD_AS_SHARED_LIB = 1
+ Settings.INCLUDE_FULL_LIBRARY = 0
+
+ def prep_dlfcn_main(self):
+ if Settings.ASM_JS:
+ Settings.MAIN_MODULE = 1
+ Settings.SIDE_MODULE = 0
+ else:
+ Settings.BUILD_AS_SHARED_LIB = 0
+ Settings.INCLUDE_FULL_LIBRARY = 1
+
+ dlfcn_post_build = '''
+def process(filename):
+ src = open(filename, 'r').read().replace(
+ '// {{PRE_RUN_ADDITIONS}}',
+ "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
+ )
+ open(filename, 'w').write(src)
+'''
+
+ def test_dlfcn_basic(self):
+ if not self.can_dlfcn(): return
+ self.prep_dlfcn_lib()
lib_src = '''
#include <cstdio>
@@ -5599,10 +5643,10 @@ The current type of b is: 9
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
src = '''
#include <cstdio>
#include <dlfcn.h>
@@ -5621,28 +5665,17 @@ The current type of b is: 9
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Constructing main object.\nConstructing lib object.\n',
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_qsort(self):
- if self.emcc_args is None: return self.skip('requires emcc')
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
-
- Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
+ if not self.can_dlfcn(): return
if Settings.USE_TYPED_ARRAYS == 2:
Settings.CORRECT_SIGNS = 1 # Needed for unsafe optimizations
+ self.prep_dlfcn_lib()
+ Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
lib_src = '''
int lib_cmp(const void* left, const void* right) {
const int* a = (const int*) left;
@@ -5660,11 +5693,11 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
- Settings.EXPORTED_FUNCTIONS = ['_get_cmp']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
src = '''
#include <stdio.h>
#include <stdlib.h>
@@ -5686,6 +5719,13 @@ def process(filename):
CMP_TYPE lib_cmp_ptr;
int arr[5] = {4, 2, 5, 1, 3};
+ qsort((void*)arr, 5, sizeof(int), main_cmp);
+ printf("Sort with main comparison: ");
+ for (int i = 0; i < 5; i++) {
+ printf("%d ", arr[i]);
+ }
+ printf("\\n");
+
lib_handle = dlopen("liblib.so", RTLD_NOW);
if (lib_handle == NULL) {
printf("Could not load lib.\\n");
@@ -5697,14 +5737,6 @@ def process(filename):
return 1;
}
lib_cmp_ptr = getter_ptr();
-
- qsort((void*)arr, 5, sizeof(int), main_cmp);
- printf("Sort with main comparison: ");
- for (int i = 0; i < 5; i++) {
- printf("%d ", arr[i]);
- }
- printf("\\n");
-
qsort((void*)arr, 5, sizeof(int), lib_cmp_ptr);
printf("Sort with lib comparison: ");
for (int i = 0; i < 5; i++) {
@@ -5715,27 +5747,22 @@ def process(filename):
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
- Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Sort with main comparison: 5 4 3 2 1 *Sort with lib comparison: 1 2 3 4 5 *',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
+
+ if Settings.ASM_JS and os.path.exists(SPIDERMONKEY_ENGINE[0]):
+ out = run_js('liblib.so', engine=SPIDERMONKEY_ENGINE, full_output=True, stderr=STDOUT)
+ if 'asm' in out:
+ self.validate_asmjs(out)
def test_dlfcn_data_and_fptr(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
- if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
+ if not self.can_dlfcn(): return
- Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
+ if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func')
+ self.prep_dlfcn_lib()
lib_src = '''
#include <stdio.h>
@@ -5760,21 +5787,23 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
Settings.EXPORTED_GLOBALS = ['_global']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
+ Settings.LINKABLE = 1
src = '''
#include <stdio.h>
#include <dlfcn.h>
+ #include <emscripten.h>
typedef void (*FUNCTYPE(int, void(*)()))();
FUNCTYPE func;
- void parent_func() {
+ void EMSCRIPTEN_KEEPALIVE parent_func() {
printf("parent_func called from child\\n");
}
@@ -5818,23 +5847,14 @@ def process(filename):
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
Settings.EXPORTED_GLOBALS = []
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'In func: 13*First calling main_fptr from lib.*Second calling lib_fptr from main.*parent_func called from child*parent_func called from child*Var: 42*',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_alias(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
Settings.LINKABLE = 1
Settings.NAMED_GLOBALS = 1
@@ -5876,29 +5896,23 @@ def process(filename):
Settings.BUILD_AS_SHARED_LIB = 0
Settings.INCLUDE_FULL_LIBRARY = 1
Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, 'Parent global: 123.*Parent global: 456.*',
output_nicerizer=lambda x, err: x.replace('\n', '*'),
- post_build=add_pre_run_and_checks,
+ post_build=self.dlfcn_post_build,
extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/time.h,libc/langinfo.h'])
Settings.INCLUDE_FULL_LIBRARY = 0
def test_dlfcn_varargs(self):
- if Settings.ASM_JS: return self.skip('TODO: dlopen in asm')
+ if Settings.ASM_JS: return self.skip('this is not a valid case - libraries should not be able to access their parents globals willy nilly')
+
+ if not self.can_dlfcn(): return
Settings.LINKABLE = 1
- Settings.NAMED_GLOBALS = 1
if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize things that prevent shared objects from working')
if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this')
+ self.prep_dlfcn_lib()
lib_src = r'''
void print_ints(int n, ...);
extern "C" void func() {
@@ -5907,15 +5921,16 @@ def process(filename):
'''
dirname = self.get_dir()
filename = os.path.join(dirname, 'liblib.cpp')
- Settings.BUILD_AS_SHARED_LIB = 1
Settings.EXPORTED_FUNCTIONS = ['_func']
self.build(lib_src, dirname, filename)
shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+ self.prep_dlfcn_main()
src = r'''
#include <stdarg.h>
#include <stdio.h>
#include <dlfcn.h>
+ #include <assert.h>
void print_ints(int n, ...) {
va_list args;
@@ -5933,24 +5948,16 @@ def process(filename):
print_ints(2, 100, 200);
lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle);
fptr = (void (*)())dlsym(lib_handle, "func");
fptr();
return 0;
}
'''
- Settings.BUILD_AS_SHARED_LIB = 0
Settings.EXPORTED_FUNCTIONS = ['_main']
- add_pre_run_and_checks = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- "FS.createLazyFile('/', 'liblib.so', 'liblib.so', true, false);"
- )
- open(filename, 'w').write(src)
-'''
self.do_run(src, '100\n200\n13\n42\n',
- post_build=add_pre_run_and_checks)
+ post_build=self.dlfcn_post_build)
def test_dlfcn_self(self):
if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1')
@@ -5959,14 +5966,15 @@ def process(filename):
src = r'''
#include <stdio.h>
#include <dlfcn.h>
+#include <emscripten.h>
-int global = 123;
+int EMSCRIPTEN_KEEPALIVE global = 123;
-extern "C" __attribute__((noinline)) void foo(int x) {
+extern "C" EMSCRIPTEN_KEEPALIVE void foo(int x) {
printf("%d\n", x);
}
-extern "C" __attribute__((noinline)) void repeatable() {
+extern "C" EMSCRIPTEN_KEEPALIVE void repeatable() {
void* self = dlopen(NULL, RTLD_LAZY);
int* global_ptr = (int*)dlsym(self, "global");
void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo");
@@ -5987,15 +5995,372 @@ return 0;
break
else:
raise Exception('Could not find symbol table!')
- import json
- table = json.loads(table[table.find('{'):table.rfind('}')+1])
- actual = list(sorted(table.keys()))
+ table = table[table.find('{'):table.rfind('}')+1]
# ensure there aren't too many globals; we don't want unnamed_addr
- assert actual == ['_foo', '_global', '_main', '_repeatable'], \
- "Symbol table does not match: %s" % actual
-
+ assert table.count(',') <= 4
self.do_run(src, '123\n123', post_build=(None, post))
+
+ def test_dlfcn_unique_sig(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = '''
+ #include <stdio.h>
+
+ int myfunc(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m) {
+ return 13;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_myfunc']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef int (*FUNCTYPE)(int, int, int, int, int, int, int, int, int, int, int, int, int);
+
+ int main() {
+ void *lib_handle;
+ FUNCTYPE func_ptr;
+
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
+ assert(func_ptr != NULL);
+ assert(func_ptr(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 13);
+
+ puts("success");
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_stacks(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ int myfunc(const char *input) {
+ char bigstack[1024] = { 0 };
+
+ // make sure we didn't just trample the stack!
+ assert(!strcmp(input, "foobar"));
+
+ snprintf(bigstack, sizeof(bigstack), input);
+ return strlen(bigstack);
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_myfunc']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = '''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef int (*FUNCTYPE)(const char *);
+
+ int main() {
+ void *lib_handle;
+ FUNCTYPE func_ptr;
+ char str[128];
+
+ snprintf(str, sizeof(str), "foobar");
+
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ func_ptr = (FUNCTYPE)dlsym(lib_handle, "myfunc");
+ assert(func_ptr != NULL);
+ assert(func_ptr(str) == 6);
+
+ puts("success");
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_funcs(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ typedef void (*voidfunc)();
+ typedef void (*intfunc)(int);
+
+ void callvoid(voidfunc f) { f(); }
+ void callint(voidfunc f, int x) { f(x); }
+
+ void void_0() { printf("void 0\n"); }
+ void void_1() { printf("void 1\n"); }
+ voidfunc getvoid(int i) {
+ switch(i) {
+ case 0: return void_0;
+ case 1: return void_1;
+ default: return NULL;
+ }
+ }
+
+ void int_0(int x) { printf("int 0 %d\n", x); }
+ void int_1(int x) { printf("int 1 %d\n", x); }
+ intfunc getint(int i) {
+ switch(i) {
+ case 0: return int_0;
+ case 1: return int_1;
+ default: return NULL;
+ }
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_callvoid', '_callint', '_getvoid', '_getint']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef void (*voidfunc)();
+ typedef void (*intfunc)(int);
+
+ typedef void (*voidcaller)(voidfunc);
+ typedef void (*intcaller)(intfunc, int);
+
+ typedef voidfunc (*voidgetter)(int);
+ typedef intfunc (*intgetter)(int);
+
+ void void_main() { printf("main.\n"); }
+ void int_main(int x) { printf("main %d\n", x); }
+
+ int main() {
+ printf("go\n");
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ voidcaller callvoid = (voidcaller)dlsym(lib_handle, "callvoid");
+ assert(callvoid != NULL);
+ callvoid(void_main);
+
+ intcaller callint = (intcaller)dlsym(lib_handle, "callint");
+ assert(callint != NULL);
+ callint(int_main, 201);
+
+ voidgetter getvoid = (voidgetter)dlsym(lib_handle, "getvoid");
+ assert(getvoid != NULL);
+ callvoid(getvoid(0));
+ callvoid(getvoid(1));
+
+ intgetter getint = (intgetter)dlsym(lib_handle, "getint");
+ assert(getint != NULL);
+ callint(getint(0), 54);
+ callint(getint(1), 9000);
+
+ assert(getint(1000) == NULL);
+
+ puts("ok");
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc']
+ self.do_run(src, '''go
+main.
+main 201
+void 0
+void 1
+int 0 54
+int 1 9000
+ok
+''', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_mallocs(self):
+ if not Settings.ASM_JS: return self.skip('needs asm')
+
+ if not self.can_dlfcn(): return
+
+ Settings.TOTAL_MEMORY = 64*1024*1024 # will be exhausted without functional malloc/free
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ void *mallocproxy(int n) { return malloc(n); }
+ void freeproxy(void *p) { free(p); }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_mallocproxy', '_freeproxy']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = open(path_from_root('tests', 'dlmalloc_proxy.c')).read()
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''*294,153*''', force_c=True, post_build=self.dlfcn_post_build)
+
+ def test_dlfcn_longjmp(self):
+ if not self.can_dlfcn(): return
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ #include <setjmp.h>
+
+ void jumpy(jmp_buf buf) {
+ static int i = 0;
+ i++;
+ if (i == 10) longjmp(buf, i);
+ printf("pre %d\n", i);
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_jumpy']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.c')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+ #include <setjmp.h>
+
+ typedef void (*jumpfunc)(jmp_buf);
+
+ int main() {
+ printf("go!\n");
+
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ jumpfunc jumpy = (jumpfunc)dlsym(lib_handle, "jumpy");
+ assert(jumpy);
+
+ jmp_buf buf;
+ int jmpval = setjmp(buf);
+ if (jmpval == 0) {
+ while (1) jumpy(buf);
+ } else {
+ printf("out!\n");
+ }
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''go!
+pre 1
+pre 2
+pre 3
+pre 4
+pre 5
+pre 6
+pre 7
+pre 8
+pre 9
+out!
+''', post_build=self.dlfcn_post_build, force_c=True)
+
+ def zzztest_dlfcn_exceptions(self): # TODO: make this work. need to forward tempRet0 across modules
+ if not self.can_dlfcn(): return
+
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+
+ self.prep_dlfcn_lib()
+ lib_src = r'''
+ extern "C" {
+ int ok() {
+ return 65;
+ }
+ int fail() {
+ throw 123;
+ }
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_ok', '_fail']
+ dirname = self.get_dir()
+ filename = os.path.join(dirname, 'liblib.cpp')
+ self.build(lib_src, dirname, filename)
+ shutil.move(filename + '.o.js', os.path.join(dirname, 'liblib.so'))
+
+ self.prep_dlfcn_main()
+ src = r'''
+ #include <assert.h>
+ #include <stdio.h>
+ #include <dlfcn.h>
+
+ typedef int (*intfunc)();
+
+ int main() {
+ printf("go!\n");
+
+ void *lib_handle;
+ lib_handle = dlopen("liblib.so", RTLD_NOW);
+ assert(lib_handle != NULL);
+
+ intfunc okk = (intfunc)dlsym(lib_handle, "ok");
+ intfunc faill = (intfunc)dlsym(lib_handle, "fail");
+ assert(okk && faill);
+
+ try {
+ printf("ok: %d\n", okk());
+ } catch(...) {
+ printf("wha\n");
+ }
+
+ try {
+ printf("fail: %d\n", faill());
+ } catch(int x) {
+ printf("int %d\n", x);
+ }
+
+ try {
+ printf("fail: %d\n", faill());
+ } catch(double x) {
+ printf("caught %f\n", x);
+ }
+
+ return 0;
+ }
+ '''
+ Settings.EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']
+ self.do_run(src, '''go!
+ok: 65
+int 123
+ok
+''', post_build=self.dlfcn_post_build)
+
def test_rand(self):
return self.skip('rand() is now random') # FIXME
@@ -6992,7 +7357,7 @@ def process(filename):
FS.registerDevice(dummy_device, {});
FS.createDataFile('/', 'file', 'abcdef', true, true);
- FS.mkdev('/device', 0666, dummy_device);
+ FS.mkdev('/device', dummy_device);
\'\'\'
)
open(filename, 'w').write(src)
@@ -7250,32 +7615,18 @@ def process(filename):
Settings.INCLUDE_FULL_LIBRARY = 0
def test_unistd_access(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'access.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_curdir(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'curdir.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'curdir.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'curdir.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_close(self):
src = open(path_from_root('tests', 'unistd', 'close.c'), 'r').read()
@@ -7302,18 +7653,11 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_truncate(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'truncate.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_swab(self):
src = open(path_from_root('tests', 'unistd', 'swab.c'), 'r').read()
@@ -7339,18 +7683,11 @@ def process(filename):
self.do_run(src, 'success', force_c=True)
def test_unistd_links(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'links.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
src = open(path_from_root('tests', 'unistd', 'links.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'links.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_sleep(self):
src = open(path_from_root('tests', 'unistd', 'sleep.c'), 'r').read()
@@ -7358,18 +7695,12 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_io(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'io.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
+ if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code
+ if not self.is_le32(): return self.skip('le32 needed for inline js')
+ if self.run_name == 'o2': return self.skip('non-asm optimized builds can fail with inline js')
src = open(path_from_root('tests', 'unistd', 'io.c'), 'r').read()
expected = open(path_from_root('tests', 'unistd', 'io.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, expected)
def test_unistd_misc(self):
src = open(path_from_root('tests', 'unistd', 'misc.c'), 'r').read()
diff --git a/tests/test_other.py b/tests/test_other.py
index fd1a6245..627995e9 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -281,54 +281,65 @@ f.close()
if os.name == 'nt':
make_command = 'mingw32-make'
- emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten.cmake')
+ generator = 'MinGW Makefiles'
+ emconfigure = path_from_root('emconfigure.bat')
else:
make_command = 'make'
- emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten_unix.cmake')
+ generator = 'Unix Makefiles'
+ emconfigure = path_from_root('emconfigure')
cmake_cases = ['target_js', 'target_html']
cmake_outputs = ['hello_world.js', 'hello_world_gles.html']
- for i in range(0, 2):
+ for i in range(0, 2): # Test both JS and HTML build outputs from CMake.
for configuration in ['Debug', 'Release']:
-
- # Create a temp workspace folder
- cmakelistsdir = path_from_root('tests', 'cmake', cmake_cases[i])
- tempdirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR)
- try:
- os.chdir(tempdirname)
-
- # Run Cmake
- cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+emscriptencmaketoolchain,
- '-DCMAKE_BUILD_TYPE=' + configuration,
- '-DCMAKE_MODULE_PATH=' + path_from_root('cmake').replace('\\', '/'),
- '-G' 'Unix Makefiles', cmakelistsdir]
- ret = Popen(cmd, stdout=PIPE, stderr=PIPE).communicate()
- if ret[1] != None and len(ret[1].strip()) > 0:
- print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics.
- if 'error' in ret[1].lower():
- print >> sys.stderr, 'Failed command: ' + ' '.join(cmd)
- print >> sys.stderr, 'Result:\n' + ret[1]
- raise Exception('cmake call failed!')
- assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!'
-
- # Build
- cmd = [make_command]
- ret = Popen(cmd, stdout=PIPE).communicate()
- if ret[1] != None and len(ret[1].strip()) > 0:
- print >> sys.stderr, ret[1] # If there were any errors, print them directly to console for diagnostics.
- if 'error' in ret[0].lower() and not '0 error(s)' in ret[0].lower():
- print >> sys.stderr, 'Failed command: ' + ' '.join(cmd)
- print >> sys.stderr, 'Result:\n' + ret[0]
- raise Exception('make failed!')
- assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i]
-
- # Run through node, if CMake produced a .js file.
- if cmake_outputs[i].endswith('.js'):
- ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0]
- assert 'hello, world!' in ret, 'Running cmake-based .js application failed!'
- finally:
- os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove.
- shutil.rmtree(tempdirname)
+ # CMake can be invoked in two ways, using 'emconfigure cmake', or by directly running 'cmake'.
+ # Test both methods.
+ for invoke_method in ['cmake', 'emconfigure']:
+
+ # Create a temp workspace folder
+ cmakelistsdir = path_from_root('tests', 'cmake', cmake_cases[i])
+ tempdirname = tempfile.mkdtemp(prefix='emscripten_test_' + self.__class__.__name__ + '_', dir=TEMP_DIR)
+ try:
+ os.chdir(tempdirname)
+
+ verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
+
+ # Run Cmake
+ if invoke_method == 'cmake':
+ # Test invoking cmake directly.
+ cmd = ['cmake', '-DCMAKE_TOOLCHAIN_FILE='+path_from_root('cmake', 'Platform', 'Emscripten.cmake'),
+ '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir]
+ else:
+ # Test invoking via 'emconfigure cmake'
+ cmd = [emconfigure, 'cmake', '-DCMAKE_BUILD_TYPE=' + configuration, '-G', generator, cmakelistsdir]
+
+ ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE, stderr=None if verbose_level >= 1 else PIPE).communicate()
+ if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0:
+ logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics.
+ if len(ret) > 1 and ret[1] != None and 'error' in ret[1].lower():
+ logging.error('Failed command: ' + ' '.join(cmd))
+ logging.error('Result:\n' + ret[1])
+ raise Exception('cmake call failed!')
+ assert os.path.exists(tempdirname + '/Makefile'), 'CMake call did not produce a Makefile!'
+
+ # Build
+ cmd = [make_command] + (['VERBOSE=1'] if verbose_level >= 3 else [])
+ ret = Popen(cmd, stdout=None if verbose_level >= 2 else PIPE).communicate()
+ if len(ret) > 1 and ret[1] != None and len(ret[1].strip()) > 0:
+ logging.error(ret[1]) # If there were any errors, print them directly to console for diagnostics.
+ if len(ret) > 0 and ret[0] != None and 'error' in ret[0].lower() and not '0 error(s)' in ret[0].lower():
+ logging.error('Failed command: ' + ' '.join(cmd))
+ logging.error('Result:\n' + ret[0])
+ raise Exception('make failed!')
+ assert os.path.exists(tempdirname + '/' + cmake_outputs[i]), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + cmake_outputs[i]
+
+ # Run through node, if CMake produced a .js file.
+ if cmake_outputs[i].endswith('.js'):
+ ret = Popen(listify(NODE_JS) + [tempdirname + '/' + cmake_outputs[i]], stdout=PIPE).communicate()[0]
+ assert 'hello, world!' in ret, 'Running cmake-based .js application failed!'
+ finally:
+ os.chdir(path_from_root('tests')) # Move away from the directory we are about to remove.
+ shutil.rmtree(tempdirname)
def test_failure_error_code(self):
for compiler in [EMCC, EMXX]:
@@ -1884,3 +1895,12 @@ you should see two lines of text in different colors and a blue rectangle
SDL_Quit called (and ignored)
done.
''' in output, output
+
+ def test_preprocess(self):
+ self.clear()
+
+ out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-E'], stdout=PIPE).communicate()
+ assert not os.path.exists('a.out.js')
+ assert '''tests/hello_world.c"''' in out
+ assert '''printf("hello, world!''' in out
+
diff --git a/tests/unistd/access.c b/tests/unistd/access.c
index 89428610..4d5ba08e 100644
--- a/tests/unistd/access.c
+++ b/tests/unistd/access.c
@@ -1,8 +1,16 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.writeFile('/forbidden', ''); FS.chmod('/forbidden', 0000);
+ FS.writeFile('/readable', ''); FS.chmod('/readable', 0444);
+ FS.writeFile('/writeable', ''); FS.chmod('/writeable', 0222);
+ FS.writeFile('/allaccess', ''); FS.chmod('/allaccess', 0777);
+ );
+
char* files[] = {"/readable", "/writeable",
"/allaccess", "/forbidden", "/nonexistent"};
for (int i = 0; i < sizeof files / sizeof files[0]; i++) {
diff --git a/tests/unistd/access.js b/tests/unistd/access.js
deleted file mode 100644
index ea9e6359..00000000
--- a/tests/unistd/access.js
+++ /dev/null
@@ -1,4 +0,0 @@
-FS.createDataFile('/', 'forbidden', '', false, false);
-FS.createDataFile('/', 'readable', '', true, false);
-FS.createDataFile('/', 'writeable', '', false, true);
-FS.createDataFile('/', 'allaccess', '', true, true);
diff --git a/tests/unistd/access.out b/tests/unistd/access.out
index dffe0b9e..d462e5a5 100644
--- a/tests/unistd/access.out
+++ b/tests/unistd/access.out
@@ -2,8 +2,8 @@ F_OK(/readable): 0
errno: 0
R_OK(/readable): 0
errno: 0
-X_OK(/readable): 0
-errno: 0
+X_OK(/readable): -1
+errno: 13
W_OK(/readable): -1
errno: 13
diff --git a/tests/unistd/curdir.c b/tests/unistd/curdir.c
index 63b9c7fe..b9f22dd7 100644
--- a/tests/unistd/curdir.c
+++ b/tests/unistd/curdir.c
@@ -2,8 +2,19 @@
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ var dummy_device = FS.makedev(64, 0);
+ FS.registerDevice(dummy_device, {});
+ FS.mkdev('/device', dummy_device);
+
+ FS.mkdir('/folder');
+ FS.symlink('/folder', '/link');
+ FS.writeFile('/file', '', { mode: 0777 });
+ );
+
char buffer[256];
printf("getwd: %s\n", getwd(buffer));
printf("errno: %d\n", errno);
diff --git a/tests/unistd/curdir.js b/tests/unistd/curdir.js
deleted file mode 100644
index 75a1d2ce..00000000
--- a/tests/unistd/curdir.js
+++ /dev/null
@@ -1,7 +0,0 @@
-var dummy_device = FS.makedev(64, 0);
-FS.registerDevice(dummy_device, {});
-
-FS.createDataFile('/', 'file', '', true, true);
-FS.createFolder('/', 'folder', true, true);
-FS.mkdev('/device', 0666, dummy_device);
-FS.createLink('/', 'link', 'folder', true, true);
diff --git a/tests/unistd/io.c b/tests/unistd/io.c
index a96290ef..0ff5f4fb 100644
--- a/tests/unistd/io.c
+++ b/tests/unistd/io.c
@@ -3,8 +3,58 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ var major = 80;
+
+ var device = FS.makedev(major++, 0);
+ FS.registerDevice(device, {
+ open: function(stream) {
+ stream.payload = [65, 66, 67, 68];
+ },
+ read: function(stream, buffer, offset, length, pos) {
+ var bytesRead = 0;
+ for (var i = 0; i < length; i++) {
+ if (stream.payload.length) {
+ bytesRead++;
+ buffer[offset+i] = stream.payload.shift();
+ } else {
+ break;
+ }
+ }
+ return bytesRead;
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ for (var i = 0; i < length; i++) {
+ Module.print('TO DEVICE: ' + buffer[offset+i]);
+ }
+ return i;
+ }
+ });
+ FS.mkdev('/device', device);
+
+ var broken_device = FS.makedev(major++, 0);
+ FS.registerDevice(broken_device, {
+ read: function(stream, buffer, offset, length, pos) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ },
+ write: function(stream, buffer, offset, length, pos) {
+ throw new FS.ErrnoError(ERRNO_CODES.EIO);
+ }
+ });
+ FS.mkdev('/broken-device', broken_device);
+
+ // NB: These are meant to test FS.createDevice specifically,
+ // and as such do not use registerDevice/mkdev
+ FS.createDevice('/', 'createDevice-read-only', function() {});
+ FS.createDevice('/', 'createDevice-write-only', null, function() {});
+
+ FS.mkdir('/folder', 0777);
+ FS.writeFile('/file', '1234567890');
+ );
+
char readBuffer[256] = {0};
char writeBuffer[] = "writeme";
diff --git a/tests/unistd/io.js b/tests/unistd/io.js
deleted file mode 100644
index 11c0da79..00000000
--- a/tests/unistd/io.js
+++ /dev/null
@@ -1,52 +0,0 @@
-(function() {
- var major = 80;
-
- var device = FS.makedev(major++, 0);
- var device_ops = {
- open: function(stream) {
- stream.payload = [65, 66, 67, 68];
- },
- read: function(stream, buffer, offset, length, pos) {
- var bytesRead = 0;
- for (var i = 0; i < length; i++) {
- if (stream.payload.length) {
- bytesRead++;
- buffer[offset+i] = stream.payload.shift();
- } else {
- break;
- }
- }
- return bytesRead;
- },
- write: function(stream, buffer, offset, length, pos) {
- for (var i = 0; i < length; i++) {
- Module.print("TO DEVICE: " + buffer[offset+i]);
- }
- return i;
- }
- };
- FS.registerDevice(device, device_ops);
-
- FS.mkdev('/device', 0666, device);
-
- var broken_device = FS.makedev(major++, 0);
- var broken_device_ops = {
- read: function(stream, buffer, offset, length, pos) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- },
- write: function(stream, buffer, offset, length, pos) {
- throw new FS.ErrnoError(ERRNO_CODES.EIO);
- }
- };
- FS.registerDevice(broken_device, broken_device_ops);
-
- FS.mkdev('/broken-device', 0666, broken_device);
-
- // NB: These are meant to test FS.createDevice specifically,
- // and as such do not use registerDevice/mkdev
- FS.createDevice('/', 'createDevice-read-only', function() {});
- FS.createDevice('/', 'createDevice-write-only', null, function() {});
-
- FS.createDataFile('/', 'file', '1234567890', true, true);
- FS.createFolder('/', 'folder', true, true);
-})();
diff --git a/tests/unistd/links.c b/tests/unistd/links.c
index c6da83b9..5b403c1f 100644
--- a/tests/unistd/links.c
+++ b/tests/unistd/links.c
@@ -1,8 +1,15 @@
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.symlink('../test/../there!', '/link');
+ FS.writeFile('/file', 'test');
+ FS.mkdir('/folder');
+ );
+
char* files[] = {"/link", "/file", "/folder"};
char buffer[256] = {0};
diff --git a/tests/unistd/links.js b/tests/unistd/links.js
deleted file mode 100644
index 5e58a729..00000000
--- a/tests/unistd/links.js
+++ /dev/null
@@ -1,3 +0,0 @@
-FS.createLink('/', 'link', '../test/../there!', true, true);
-FS.createDataFile('/', 'file', 'test', true, true);
-FS.createFolder('/', 'folder', true, true);
diff --git a/tests/unistd/truncate.c b/tests/unistd/truncate.c
index 18920976..b1d9fc96 100644
--- a/tests/unistd/truncate.c
+++ b/tests/unistd/truncate.c
@@ -4,8 +4,15 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
+#include <emscripten.h>
int main() {
+ EM_ASM(
+ FS.writeFile('/towrite', 'abcdef');
+ FS.writeFile('/toread', 'abcdef');
+ FS.chmod('/toread', 0444);
+ );
+
struct stat s;
int f = open("/towrite", O_WRONLY);
int f2 = open("/toread", O_RDONLY);
diff --git a/tests/unistd/truncate.js b/tests/unistd/truncate.js
deleted file mode 100644
index 6a4c6868..00000000
--- a/tests/unistd/truncate.js
+++ /dev/null
@@ -1,2 +0,0 @@
-FS.createDataFile('/', 'towrite', 'abcdef', true, true);
-FS.createDataFile('/', 'toread', 'abcdef', true, false);
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index b42164f9..c029ab4e 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1564,6 +1564,7 @@ function normalizeAsm(func) {
var data = {
params: {}, // ident => ASM_* type
vars: {}, // ident => ASM_* type
+ inlines: [], // list of inline assembly copies
};
// process initial params
var stats = func[3];
@@ -1621,6 +1622,10 @@ function normalizeAsm(func) {
node[0] = 'name';
node[1] = 'Math_' + node[2];
}
+ } else if (type === 'call' && node[1][0] === 'function') {
+ assert(!node[1][1]); // anonymous functions only
+ data.inlines.push(node[1]);
+ node[1] = ['name', 'inlinejs']; // empty out body, leave arguments, so they are eliminated/minified properly
}
});
i++;
@@ -1669,6 +1674,14 @@ function denormalizeAsm(func, data) {
} else {
stats[next] = emptyNode();
}
+ if (data.inlines.length > 0) {
+ var i = 0;
+ traverse(func, function(node, type) {
+ if (type === 'call' && node[1][0] === 'name' && node[1][1] === 'inlinejs') {
+ node[1] = data.inlines[i++]; // swap back in the body
+ }
+ });
+ }
//printErr('denormalized \n\n' + astToSrc(func) + '\n\n');
}
@@ -2019,6 +2032,7 @@ function registerize(ast) {
var finalAsmData = {
params: {},
vars: {},
+ inlines: asmData.inlines,
};
for (var i = 1; i < nextReg; i++) {
var reg = fullNames[i];
@@ -3018,6 +3032,9 @@ function outline(ast) {
}
var ignore = [];
traverse(func, function(node) {
+ if (node[0] === 'while' && node[2][0] !== 'block') {
+ node[2] = ['block', [node[2]]]; // so we have a list of statements and can flatten while(1) switch
+ }
var stats = getStatements(node);
if (stats) {
for (var i = 0; i < stats.length; i++) {
@@ -3452,7 +3469,7 @@ function outline(ast) {
});
// finalize
var newFunc = ['defun', newIdent, ['sp'], code];
- var newAsmData = { params: { sp: ASM_INT }, vars: {} };
+ var newAsmData = { params: { sp: ASM_INT }, vars: {}, inlines: asmData.inlines };
for (var v in codeInfo.reads) {
if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
}
@@ -3694,8 +3711,10 @@ function outline(ast) {
}
}
}
- ret.push(func);
- printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n');
+ if (ret) {
+ ret.push(func);
+ printErr('... resulting sizes of ' + func[1] + ' is ' + ret.map(measureSize) + '\n');
+ }
}
denormalizeAsm(func, asmData);
});
diff --git a/tools/jsrun.py b/tools/jsrun.py
index 91038f6e..6f77ce51 100644
--- a/tools/jsrun.py
+++ b/tools/jsrun.py
@@ -10,6 +10,7 @@ def timeout_run(proc, timeout, note='unnamed process', full_output=False):
proc.kill() # XXX bug: killing emscripten.py does not kill it's child process!
raise Exception("Timed out: " + note)
out = proc.communicate()
+ out = map(lambda o: '' if o is None else o, out)
return '\n'.join(out) if full_output else out[0]
def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None, full_output=False):
diff --git a/tools/shared.py b/tools/shared.py
index 3ee5db23..8031d99c 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -283,6 +283,20 @@ def check_node_version():
logging.warning('cannot check node version: %s', e)
return False
+# Finds the system temp directory without resorting to using the one configured in .emscripten
+def find_temp_directory():
+ if WINDOWS:
+ if os.getenv('TEMP') and os.path.isdir(os.getenv('TEMP')):
+ return os.getenv('TEMP')
+ elif os.getenv('TMP') and os.path.isdir(os.getenv('TMP')):
+ return os.getenv('TMP')
+ elif os.path.isdir('C:\\temp'):
+ return os.getenv('C:\\temp')
+ else:
+ return None # No luck!
+ else:
+ return '/tmp'
+
# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM)
# exists.
# The test runner always does this check (through |force|). emcc does this less frequently,
@@ -429,7 +443,6 @@ EMCC = path_from_root('emcc')
EMXX = path_from_root('em++')
EMAR = path_from_root('emar')
EMRANLIB = path_from_root('emranlib')
-EMLIBTOOL = path_from_root('emlibtool')
EMCONFIG = path_from_root('em-config')
EMLINK = path_from_root('emlink.py')
EMMAKEN = path_from_root('tools', 'emmaken.py')
@@ -451,8 +464,13 @@ class Configuration:
try:
self.TEMP_DIR = TEMP_DIR
except NameError:
- logging.debug('TEMP_DIR not defined in ~/.emscripten, using /tmp')
- self.TEMP_DIR = '/tmp'
+ self.TEMP_DIR = find_temp_directory()
+ if self.TEMP_DIR == None:
+ logging.critical('TEMP_DIR not defined in ' + os.path.expanduser('~\\.emscripten') + ", and could not detect a suitable directory! Please configure .emscripten to contain a variable TEMP_DIR='/path/to/temp/dir'.")
+ logging.debug('TEMP_DIR not defined in ~/.emscripten, using ' + self.TEMP_DIR)
+
+ if not os.path.isdir(self.TEMP_DIR):
+ logging.critical("The temp directory TEMP_DIR='" + self.TEMP_DIR + "' doesn't seem to exist! Please make sure that the path is correct.")
self.CANONICAL_TEMP_DIR = os.path.join(self.TEMP_DIR, 'emscripten_temp')
@@ -470,12 +488,13 @@ class Configuration:
save_debug_files=os.environ.get('EMCC_DEBUG_SAVE'))
def apply_configuration():
- global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR
+ global configuration, DEBUG, EMSCRIPTEN_TEMP_DIR, DEBUG_CACHE, CANONICAL_TEMP_DIR, TEMP_DIR
configuration = Configuration()
DEBUG = configuration.DEBUG
EMSCRIPTEN_TEMP_DIR = configuration.EMSCRIPTEN_TEMP_DIR
DEBUG_CACHE = configuration.DEBUG_CACHE
CANONICAL_TEMP_DIR = configuration.CANONICAL_TEMP_DIR
+ TEMP_DIR = configuration.TEMP_DIR
apply_configuration()
logging.basicConfig(format='%(levelname)-8s %(name)s: %(message)s')
@@ -527,15 +546,13 @@ def get_llvm_target():
return os.environ.get('EMCC_LLVM_TARGET') or 'le32-unknown-nacl' # 'i386-pc-linux-gnu'
LLVM_TARGET = get_llvm_target()
+# COMPILER_OPTS: options passed to clang when generating bitcode for us
try:
COMPILER_OPTS # Can be set in EM_CONFIG, optionally
except:
COMPILER_OPTS = []
-# Force a simple, standard target as much as possible: target 32-bit linux, and disable various flags that hint at other platforms
-COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-U__i386__', '-U__i386', '-Ui386',
- '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
- '-DEMSCRIPTEN', '-D__EMSCRIPTEN__', '-U__STRICT_ANSI__',
- '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno',
+COMPILER_OPTS = COMPILER_OPTS + ['-m32', '-DEMSCRIPTEN', '-D__EMSCRIPTEN__',
+ '-fno-math-errno',
#'-fno-threadsafe-statics', # disabled due to issue 1289
'-target', LLVM_TARGET]
@@ -543,6 +560,11 @@ if LLVM_TARGET == 'le32-unknown-nacl':
COMPILER_OPTS = filter(lambda opt: opt != '-m32', COMPILER_OPTS) # le32 target is 32-bit anyhow, no need for -m32
COMPILER_OPTS += ['-U__native_client__', '-U__pnacl__', '-U__ELF__'] # The nacl target is originally used for Google Native Client. Emscripten is not NaCl, so remove the platform #define, when using their triple.
+# Remove various platform specific defines, and set little endian
+COMPILER_STANDARDIZATION_OPTS = ['-U__i386__', '-U__i386', '-Ui386', '-U__STRICT_ANSI__', '-D__IEEE_LITTLE_ENDIAN',
+ '-U__SSE__', '-U__SSE_MATH__', '-U__SSE2__', '-U__SSE2_MATH__', '-U__MMX__',
+ '-U__APPLE__', '-U__linux__']
+
USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK')
if USE_EMSDK:
@@ -559,9 +581,8 @@ if USE_EMSDK:
'-Xclang', '-isystem' + path_from_root('system', 'include', 'gfx'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'net'),
'-Xclang', '-isystem' + path_from_root('system', 'include', 'SDL'),
- ] + [
- '-U__APPLE__', '-U__linux__'
]
+ EMSDK_OPTS += COMPILER_STANDARDIZATION_OPTS
if LLVM_TARGET != 'le32-unknown-nacl':
EMSDK_CXX_OPTS = ['-nostdinc++'] # le32 target does not need -nostdinc++
else:
@@ -570,6 +591,7 @@ if USE_EMSDK:
else:
EMSDK_OPTS = []
EMSDK_CXX_OPTS = []
+ COMPILER_OPTS += COMPILER_STANDARDIZATION_OPTS
#print >> sys.stderr, 'SDK opts', ' '.join(EMSDK_OPTS)
#print >> sys.stderr, 'Compiler opts', ' '.join(COMPILER_OPTS)
@@ -717,6 +739,10 @@ class Settings2(type):
return ret
@classmethod
+ def copy(self, values):
+ self.attrs = values
+
+ @classmethod
def apply_opt_level(self, opt_level, noisy=False):
if opt_level >= 1:
self.attrs['ASM_JS'] = 1
@@ -782,7 +808,6 @@ class Building:
env['LD'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['LDSHARED'] = EMCC if not WINDOWS else 'python %r' % EMCC
env['RANLIB'] = EMRANLIB if not WINDOWS else 'python %r' % EMRANLIB
- #env['LIBTOOL'] = EMLIBTOOL if not WINDOWS else 'python %r' % EMLIBTOOL
env['EMMAKEN_COMPILER'] = Building.COMPILER
env['EMSCRIPTEN_TOOLS'] = path_from_root('tools')
env['CFLAGS'] = env['EMMAKEN_CFLAGS'] = ' '.join(Building.COMPILER_TEST_OPTS)
@@ -797,33 +822,12 @@ class Building:
@staticmethod
def handle_CMake_toolchain(args, env):
- CMakeToolchain = ('''# the name of the target operating system
-SET(CMAKE_SYSTEM_NAME Linux)
-
-# which C and C++ compiler to use
-SET(CMAKE_C_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/emcc)
-SET(CMAKE_CXX_COMPILER %(winfix)s$EMSCRIPTEN_ROOT/em++)
-SET(CMAKE_AR %(winfix)s$EMSCRIPTEN_ROOT/emar)
-SET(CMAKE_RANLIB %(winfix)s$EMSCRIPTEN_ROOT/emranlib)
-SET(CMAKE_C_FLAGS $CFLAGS)
-SET(CMAKE_CXX_FLAGS $CXXFLAGS)
-
-# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH $EMSCRIPTEN_ROOT/system/include )
-
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search
-# programs in the host environment
-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)''' % { 'winfix': '' if not WINDOWS else 'python ' }) \
- .replace('$EMSCRIPTEN_ROOT', path_from_root('').replace('\\', '/')) \
- .replace('$CFLAGS', env['CFLAGS']) \
- .replace('$CXXFLAGS', env['CFLAGS'])
- toolchainFile = mkstemp(suffix='.cmaketoolchain.txt', dir=configuration.TEMP_DIR)[1]
- open(toolchainFile, 'w').write(CMakeToolchain)
- args.append('-DCMAKE_TOOLCHAIN_FILE=%s' % os.path.abspath(toolchainFile))
+ # Don't append a toolchain file if the user specified one already.
+ for arg in args:
+ if '-DCMAKE_TOOLCHAIN_FILE' in arg:
+ return args
+
+ args.append('-DCMAKE_TOOLCHAIN_FILE=' + path_from_root('cmake', 'Platform', 'Emscripten.cmake'))
return args
@staticmethod
@@ -843,6 +847,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
raise
del env['EMMAKEN_JUST_CONFIGURE']
if process.returncode is not 0:
+ logging.error('Configure step failed with non-zero return code ' + str(process.returncode) + '! Command line: ' + str(args))
raise subprocess.CalledProcessError(cmd=args, returncode=process.returncode)
@staticmethod
@@ -893,12 +898,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
# except:
# pass
env = Building.get_building_env(native)
+ verbose_level = int(os.getenv('EM_BUILD_VERBOSE')) if os.getenv('EM_BUILD_VERBOSE') != None else 0
for k, v in env_init.iteritems():
env[k] = v
if configure: # Useful in debugging sometimes to comment this out (and the lines below up to and including the |link| call)
try:
- Building.configure(configure + configure_args, stdout=open(os.path.join(project_dir, 'configure_'), 'w'),
- stderr=open(os.path.join(project_dir, 'configure_err'), 'w'), env=env)
+ Building.configure(configure + configure_args, env=env, stdout=open(os.path.join(project_dir, 'configure_'), 'w') if verbose_level < 2 else None,
+ stderr=open(os.path.join(project_dir, 'configure_err'), 'w') if verbose_level < 1 else None)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
def open_make_out(i, mode='r'):
@@ -906,13 +912,16 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def open_make_err(i, mode='r'):
return open(os.path.join(project_dir, 'make_err' + str(i)), mode)
-
+
+ if verbose_level >= 3:
+ make_args += ['VERBOSE=1']
+
for i in range(2): # FIXME: Sad workaround for some build systems that need to be run twice to succeed (e.g. poppler)
with open_make_out(i, 'w') as make_out:
with open_make_err(i, 'w') as make_err:
try:
- Building.make(make + make_args, stdout=make_out,
- stderr=make_err, env=env)
+ Building.make(make + make_args, stdout=make_out if verbose_level < 2 else None,
+ stderr=make_err if verbose_level < 1 else None, env=env)
except subprocess.CalledProcessError, e:
pass # Ignore exit code != 0
try:
@@ -924,10 +933,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
break
except Exception, e:
if i > 0:
- # Due to the ugly hack above our best guess is to output the first run
- with open_make_err(0) as ferr:
- for line in ferr:
- sys.stderr.write(line)
+ if verbose_level == 0:
+ # Due to the ugly hack above our best guess is to output the first run
+ with open_make_err(0) as ferr:
+ for line in ferr:
+ sys.stderr.write(line)
raise Exception('could not build library ' + name + ' due to exception ' + str(e))
if old_dir:
os.chdir(old_dir)
@@ -1176,8 +1186,6 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
def get_safe_internalize():
if not Building.can_build_standalone(): return [] # do not internalize anything
exps = expand_response(Settings.EXPORTED_FUNCTIONS)
- if '_malloc' not in exps: exps.append('_malloc') # needed internally, even if user did not add to EXPORTED_FUNCTIONS
- if '_free' not in exps: exps.append('_free')
exports = ','.join(map(lambda exp: exp[1:], exps))
# internalize carefully, llvm 3.2 will remove even main if not told not to
return ['-internalize', '-internalize-public-api-list=' + exports]
@@ -1426,7 +1434,7 @@ JCache = cache.JCache(Cache)
chunkify = cache.chunkify
class JS:
- memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASE+]+)\)'
+ memory_initializer_pattern = '/\* memory initializer \*/ allocate\(([\d,\.concat\(\)\[\]\\n ]+)"i8", ALLOC_NONE, ([\dRuntime\.GLOBAL_BASEH+]+)\)'
no_memory_initializer_pattern = '/\* no memory initializer \*/'
memory_staticbump_pattern = 'STATICTOP = STATIC_BASE \+ (\d+);'
@@ -1438,11 +1446,32 @@ class JS:
return ident.replace('%', '$').replace('@', '_')
@staticmethod
+ def make_extcall(sig, named=True):
+ args = ','.join(['a' + str(i) for i in range(1, len(sig))])
+ args = 'index' + (',' if args else '') + args
+ # C++ exceptions are numbers, and longjmp is a string 'longjmp'
+ ret = '''function%s(%s) {
+ %sModule["dynCall_%s"](%s);
+}''' % ((' extCall_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(') {\n', ''') {
+ try {
+ var preStack = asm.stackSave();
+''').replace(';\n}', ''';
+ } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+ return ret
+
+ @staticmethod
def make_invoke(sig, named=True):
args = ','.join(['a' + str(i) for i in range(1, len(sig))])
args = 'index' + (',' if args else '') + args
# C++ exceptions are numbers, and longjmp is a string 'longjmp'
- return '''function%s(%s) {
+ ret = '''function%s(%s) {
try {
%sModule["dynCall_%s"](%s);
} catch(e) {
@@ -1451,6 +1480,17 @@ class JS:
}
}''' % ((' invoke_' + sig) if named else '', args, 'return ' if sig[0] != 'v' else '', sig, args)
+ if Settings.DLOPEN_SUPPORT and Settings.ASSERTIONS:
+ # guard against cross-module stack leaks
+ ret = ret.replace(' try {', ''' var preStack = asm.stackSave();
+ try {
+''').replace(' }\n}', ''' } finally {
+ assert(asm.stackSave() == preStack);
+ }
+}''')
+
+ return ret
+
@staticmethod
def align(x, by):
while x % by != 0: x += 1
diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js
index 612da16a..27f93d8a 100644
--- a/tools/test-js-optimizer-asm-outline1-output.js
+++ b/tools/test-js-optimizer-asm-outline1-output.js
@@ -372,6 +372,57 @@ function switchh() {
}
STACKTOP = sp;
}
+function switchh2() {
+ var helper$0 = 0, helper$1 = 0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 280 | 0;
+ while (1) {
+ helper$0 = 1;
+ helper$1 = x;
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 0:
+ f(0);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ switchh2$2(sp);
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ tempValue = HEAP32[sp + 40 >> 2] | 0;
+ tempInt = HEAP32[sp + 44 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 44 >> 2];
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ if ((tempValue | 0) == 5) {
+ STACKTOP = sp;
+ return;
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 32 >> 2] = 0;
+ HEAP32[sp + 36 >> 2] = 0;
+ switchh2$1(sp);
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ if (helper$0) {
+ helper$0 = 0;
+ HEAP32[sp + 16 >> 2] = helper$1;
+ HEAP32[sp + 24 >> 2] = 0;
+ HEAP32[sp + 28 >> 2] = 0;
+ switchh2$0(sp);
+ }
+ }
+ STACKTOP = sp;
+}
function lin$0(sp) {
sp = sp | 0;
c(14);
@@ -793,4 +844,86 @@ function switchh$2(sp) {
} while (0);
HEAP32[sp + 8 >> 2] = helper$0;
}
+function switchh2$0(sp) {
+ sp = sp | 0;
+ var helper$1 = 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ switch (helper$1 | 0) {
+ case 4:
+ f(4);
+ g();
+ case 5:
+ f(5);
+ g();
+ case 6:
+ f(6);
+ g();
+ default:
+ print(9);
+ }
+}
+function switchh2$1(sp) {
+ sp = sp | 0;
+ var helper$0 = 0, helper$1 = 0;
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 3:
+ f(3);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ HEAP32[sp + 8 >> 2] = helper$0;
+}
+function switchh2$2(sp) {
+ sp = sp | 0;
+ var helper$0 = 0, helper$1 = 0;
+ helper$0 = HEAP32[sp + 8 >> 2] | 0;
+ helper$1 = HEAP32[sp + 16 >> 2] | 0;
+ OL : do {
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 1:
+ f(1);
+ g();
+ return;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ if (helper$0) {
+ helper$0 = 0;
+ switch (helper$1 | 0) {
+ case 2:
+ f(2);
+ g();
+ break;
+ default:
+ {
+ helper$0 = 1;
+ }
+ }
+ }
+ } while (0);
+ HEAP32[sp + 8 >> 2] = helper$0;
+}
diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js
index 4282ec8e..b7ec9011 100644
--- a/tools/test-js-optimizer-asm-outline1.js
+++ b/tools/test-js-optimizer-asm-outline1.js
@@ -307,5 +307,45 @@ function switchh() {
}
}
}
+function switchh2() {
+ while (1) switch (x) {
+ case 0:
+ f(0);
+ g();
+ break;
+ case 1:
+ f(1);
+ g();
+ return;
+ case 2:
+ f(2);
+ g();
+ break;
+ case 21: // gotta keem em unseparated
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 3: // these too
+ f(3);
+ g();
+ break;
+ case 4:
+ f(4);
+ g();
+ case 5:
+ f(5);
+ g();
+ case 6:
+ f(6);
+ g();
+ default:
+ print(9);
+ }
+}
// EMSCRIPTEN_GENERATED_FUNCTIONS
// EXTRA_INFO: { "sizeToOutline": 30, "allowCostlyOutlines": 1 }