diff --git a/.gitignore b/.gitignore index 7a0f2f78c08ef..eaaa4ed59fa0a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ *~ *.bc +# Ignore generated files +src/relooper.js +src/relooper.js.raw.js diff --git a/AUTHORS b/AUTHORS index 554d7bfdcd33f..710ab203505c1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -40,4 +40,7 @@ a license to everyone to use it as detailed in LICENSE.) * Lars Schneider (copyright owned by Autodesk, Inc.) * Joel Martin * Manuel Wellmann (copyright owned by Autodesk, Inc.) +* Xuejie Xiao +* Dominic Wong +* Alan Kligman (copyright owned by Mozilla Foundation) diff --git a/cmake/Platform/Emscripten.cmake b/cmake/Platform/Emscripten.cmake new file mode 100644 index 0000000000000..532e5d996c188 --- /dev/null +++ b/cmake/Platform/Emscripten.cmake @@ -0,0 +1,44 @@ +# This file is a 'toolchain description file' for CMake. +# It teaches CMake about the Emscripten compiler, so that CMake can generate Unix 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=/cmake/Platform/Emscripten.cmake +# -DCMAKE_MODULE_PATH=/cmake +# -DCMAKE_BUILD_TYPE= +# -G "Unix Makefiles" +# # 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) +set(CMAKE_SYSTEM_VERSION 1) + +if ("${EMSCRIPTEN_ROOT_PATH}" STREQUAL "") + set(CMAKE_FIND_ROOT_PATH "$ENV{EMSCRIPTEN}") +else() + set(CMAKE_FIND_ROOT_PATH "${EMSCRIPTEN_ROOT_PATH}") +endif() + +# Specify the compilers to use for C and C++ +if ("${CMAKE_C_COMPILER}" STREQUAL "") + set(CMAKE_C_COMPILER "emcc.exe") + set(CMAKE_CXX_COMPILER "em++.exe") + set(CMAKE_AR "emar.exe") + set(CMAKE_RANLIB "emranlib.exe") +endif() + +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) + +# 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 -emit-llvm ") +set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER} -o -emit-llvm ") + +# 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) diff --git a/cmake/Platform/Emscripten_unix.cmake b/cmake/Platform/Emscripten_unix.cmake new file mode 100644 index 0000000000000..92a21fd1fbd22 --- /dev/null +++ b/cmake/Platform/Emscripten_unix.cmake @@ -0,0 +1,24 @@ +# 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/em++ b/em++ index be2ed37b07801..ba09e1a2d472d 100755 --- a/em++ +++ b/em++ @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' See emcc.py. This script forwards to there, noting that we want C++ and not C by default @@ -8,5 +8,5 @@ import os, subprocess, sys from tools import shared os.environ['EMMAKEN_CXX'] = '1' -exit(subprocess.call(['python', shared.EMCC] + sys.argv[1:])) +exit(subprocess.call([shared.PYTHON, shared.EMCC] + sys.argv[1:])) diff --git a/em-config b/em-config index dee399ed99d66..785b04e5a553d 100755 --- a/em-config +++ b/em-config @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' This is a helper tool which is designed to make it possible diff --git a/emar b/emar index 46bc65d7816e9..60498b8f24ff9 100755 --- a/emar +++ b/emar @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' emar - ar helper script diff --git a/emcc b/emcc index 29855f0372f69..9747dc68e36e1 100755 --- a/emcc +++ b/emcc @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' emcc - compiler helper script @@ -116,7 +116,7 @@ shared.check_sanity(force=DEBUG) if len(sys.argv) == 1: print 'emcc: no input files' - exit(0) + exit(1) if sys.argv[1] == '--version': print '''emcc (Emscripten GCC-like replacement) 2.0 @@ -322,10 +322,19 @@ Options that are modified or new in %s include: -v to Clang, and also enable EMCC_DEBUG to details emcc's operations - --remove-duplicates If set, will remove duplicate symbols when - linking. This can be useful because - llvm-link's behavior is not as permissive - as ld is. + --jcache Use a JavaScript cache. This is disabled by + default. When enabled, emcc will store the + results of compilation in a cache and check + the cache when compiling later, something + like what ccache does. This allows incremental + builds - where you are compiling a large + program but only modified a small part of it - + to be much faster (at the cost of more disk + IO for cache accesses). Note that you need + to enable --jcache for both loading and saving + of data, so you must enable it on a full build + for a later incremental build (where you also + enable it) to be sped up. --clear-cache Manually clears the cache of compiled emscripten system libraries (libc++, @@ -336,7 +345,10 @@ Options that are modified or new in %s include: mechanism can get confused. Clearing the cache can fix weird problems related to cache incompatibilities, like clang failing - to link with library files. + to link with library files. This also clears + other cached data like the jcache and + the bootstrapped relooper. After the cache + is cleared, this process will exit. The target file, if specified (-o ), defines what will be generated: @@ -357,10 +369,12 @@ or LLVM assembly files in human-readable form. emcc is affected by several environment variables. For details, view the source of emcc (search for 'os.environ'). +emcc: supported targets: llvm bitcode, javascript, NOT elf +(autoconf likes to see elf above to enable shared object support) ''' % (this, this, this) exit(0) elif len(sys.argv) == 2 and sys.argv[1] == '-v': # -v with no inputs - print 'emcc (Emscripten GCC-like replacement) 2.0' + print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0' exit(subprocess.call([shared.CLANG, '-v'])) def is_minus_s_for_emcc(newargs,i): @@ -523,6 +537,15 @@ for i in range(len(sys.argv)-1): sys.argv = sys.argv[:i] + sys.argv[i+2:] break +specified_target = target +target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate +target_basename = unsuffixed_basename(target) + +if '.' in target: + final_suffix = target.split('.')[-1] +else: + final_suffix = '' + if header: # header or such if len(sys.argv) >= 3: # if there is a source and a target, then copy, otherwise do nothing sys.argv = filter(lambda arg: not arg.startswith('-I'), sys.argv) @@ -568,9 +591,9 @@ try: ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] - remove_duplicates = False keep_debug = False bind = False + jcache = False def check_bad_eq(arg): assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)' @@ -578,6 +601,7 @@ try: absolute_warning_shown = False for i in range(len(newargs)): + newargs[i] = newargs[i].strip() # On Windows Vista (and possibly others), excessive spaces in the command line leak into the items in this array, so trim e.g. 'foo.cpp ' -> 'foo.cpp' if newargs[i].startswith('-O'): requested_level = newargs[i][2] if requested_level == 's': @@ -662,6 +686,7 @@ try: elif newargs[i] == '-v': shared.COMPILER_OPTS += ['-v'] DEBUG = 1 + os.environ['EMCC_DEBUG'] = '1' # send to child processes too newargs[i] = '' elif newargs[i].startswith('--shell-file'): check_bad_eq(newargs[i]) @@ -674,12 +699,16 @@ try: newargs[i] = '' newargs[i+1] = '' elif newargs[i] == '--remove-duplicates': - remove_duplicates = True + print >> sys.stderr, 'emcc: warning: --remove-duplicates is deprecated as it is no longer needed. If you cannot link without it, file a bug with a testcase' + newargs[i] = '' + elif newargs[i] == '--jcache': + jcache = True newargs[i] = '' elif newargs[i] == '--clear-cache': newargs[i] = '' print >> sys.stderr, 'emcc: clearing cache' shared.Cache.erase() + sys.exit(0) elif newargs[i].startswith(('-I/', '-L/')): if not absolute_warning_shown: print >> sys.stderr, 'emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' # Of course an absolute path to a non-system-specific library or header is fine, and you can ignore this warning. The danger are system headers that are e.g. x86 specific and nonportable. The emscripten bundled headers are modified to be portable, local system ones are generally not @@ -693,6 +722,8 @@ try: minify_whitespace = closure # if closure is run, minify whitespace if opt_level <= 0: keep_debug = True # always keep debug in -O0 + if DEBUG: start_time = time.time() # done after parsing arguments, which might affect debug state + if closure: assert os.path.exists(shared.CLOSURE_COMPILER), 'emcc: fatal: Closure compiler (%s) does not exist' % shared.CLOSURE_COMPILER @@ -725,9 +756,9 @@ try: if i > 0: prev = newargs[i-1] - if prev in ['-MT', '-install_name']: continue # ignore this gcc-style argument + if prev in ['-MT', '-install_name', '-I', '-L']: continue # ignore this gcc-style argument - if arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg): # we already removed -o , so all these should be inputs + if not arg.startswith('-') and (arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg)): # we already removed -o , so all these should be inputs newargs[i] = '' if os.path.exists(arg): if arg.endswith(SOURCE_SUFFIXES): @@ -750,7 +781,8 @@ try: else: print >> sys.stderr, 'emcc: %s: warning: Not valid LLVM bitcode' % arg else: - print >> sys.stderr, 'emcc: %s: warning: No such file or directory' % arg + print >> sys.stderr, 'emcc: %s: error: No such file or directory' % arg + exit(1) elif arg.startswith('-L'): lib_dirs.append(arg[2:]) newargs[i] = '' @@ -762,6 +794,18 @@ try: newargs = [ arg for arg in newargs if arg is not '' ] + # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode + has_dash_c = '-c' in newargs + if has_dash_c: + assert has_source_inputs, 'Must have source code inputs to use -c' + target = target_basename + '.o' + final_suffix = 'o' + + # do not link in libs when just generating object code (not an 'executable', i.e. JS, or a library) + if ('.' + final_suffix) in BITCODE_SUFFIXES and len(libs) > 0: + print >> sys.stderr, 'emcc: warning: not linking against libraries since only compiling to bitcode' + libs = [] + # Find library files for lib in libs: if DEBUG: print >> sys.stderr, 'emcc: looking for library "%s"' % lib @@ -789,22 +833,6 @@ try: newargs += CC_ADDITIONAL_ARGS - specified_target = target - target = specified_target if specified_target is not None else 'a.out.js' # specified_target is the user-specified one, target is what we will generate - - target_basename = unsuffixed_basename(target) - - # -c means do not link in gcc, and for us, the parallel is to not go all the way to JS, but stop at bitcode - has_dash_c = '-c' in newargs - if has_dash_c: - assert has_source_inputs, 'Must have source code inputs to use -c' - target = target_basename + '.o' - - if '.' in target: - final_suffix = target.split('.')[-1] - else: - final_suffix = '' - assert not (Compression.on and final_suffix != 'html'), 'Compression only works when generating HTML' # If we are using embind and generating JS, now is the time to link in bind.cpp @@ -877,8 +905,8 @@ try: assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files) # We have a specified target (-o ), which is not JavaScript or HTML, and # we have multiple files: Link them - if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files) - shared.Building.link(temp_files, specified_target, remove_duplicates=remove_duplicates) + if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files), specified_target + shared.Building.link(temp_files, specified_target) exit(0) ## Continue on to create JavaScript @@ -897,9 +925,9 @@ try: # dlmalloc def create_dlmalloc(): if DEBUG: print >> sys.stderr, 'emcc: building dlmalloc for cache' - execute(shared.ENV_PREFIX + ['python', shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr) + execute([shared.PYTHON, shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr) # we include the libc++ new stuff here, so that the common case of using just new/delete is quick to link - execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr) + execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr) shared.Building.link([in_temp('dlmalloc.o'), in_temp('new.o')], in_temp('dlmalloc_full.o')) return in_temp('dlmalloc_full.o') def fix_dlmalloc(): @@ -922,7 +950,7 @@ try: os = [] for src in ['algorithm.cpp', 'condition_variable.cpp', 'future.cpp', 'iostream.cpp', 'memory.cpp', 'random.cpp', 'stdexcept.cpp', 'system_error.cpp', 'utility.cpp', 'bind.cpp', 'debug.cpp', 'hash.cpp', 'mutex.cpp', 'string.cpp', 'thread.cpp', 'valarray.cpp', 'chrono.cpp', 'exception.cpp', 'ios.cpp', 'locale.cpp', 'regex.cpp', 'strstream.cpp', 'typeinfo.cpp']: o = in_temp(src + '.o') - execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', src), '-o', o], stdout=stdout, stderr=stderr) + execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) shared.Building.link(os, in_temp('libcxx.bc')) return in_temp('libcxx.bc') @@ -941,7 +969,7 @@ try: os = [] for src in ['private_typeinfo.cpp']: o = in_temp(src + '.o') - execute(shared.ENV_PREFIX + ['python', shared.EMXX, shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src), '-o', o], stdout=stdout, stderr=stderr) + execute([shared.PYTHON, shared.EMXX, shared.path_from_root('system', 'lib', 'libcxxabi', 'src', src), '-o', o], stdout=stdout, stderr=stderr) os.append(o) shared.Building.link(os, in_temp('libcxxabi.bc')) return in_temp('libcxxabi.bc') @@ -958,20 +986,32 @@ try: for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), ('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols), ('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]: - need = [] - has = [] + need = set() + has = set() for temp_file in temp_files: symbols = shared.Building.llvm_nm(temp_file) for library_symbol in library_symbols: if library_symbol in symbols.undefs: - need.append(library_symbol) + need.add(library_symbol) if library_symbol in symbols.defs: - has.append(library_symbol) - if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need |%s| and have |%s|' % (name, str(need), str(has)) - if force or (need and not has): + has.add(library_symbol) + for haz in has: # remove symbols that are supplied by another of the inputs + if haz in need: + need.remove(haz) + if DEBUG: print >> sys.stderr, 'emcc: considering including %s: we need %s and have %s' % (name, str(need), str(has)) + if force or len(need) > 0: # We need to build and link the library in if DEBUG: print >> sys.stderr, 'emcc: including %s' % name - extra_files_to_link.append(shared.Cache.get(name, create)) + libfile = shared.Cache.get(name, create) + if len(has) > 0: + # remove the symbols we do not need + fixed = in_temp(uniquename(libfile)) + '.bc' + shutil.copyfile(libfile, fixed) + for haz in has: + if DEBUG: print >> sys.stderr, 'emcc: including: removing symbol "%s" that we have' % haz + shared.Building.remove_symbol(fixed, haz) + libfile = fixed + extra_files_to_link.append(libfile) force = True if fix: fix() @@ -981,7 +1021,7 @@ try: (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): linker_inputs = temp_files + extra_files_to_link if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs - shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), remove_duplicates=remove_duplicates) + shared.Building.link(linker_inputs, in_temp(target_basename + '.bc')) final = in_temp(target_basename + '.bc') else: if not LEAVE_INPUTS_RAW: @@ -1015,13 +1055,14 @@ try: if DEBUG: save_intermediate('opt', 'bc') # Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript) if shared.Building.can_build_standalone(): + # If we can LTO, do it before dce, since it opens up dce opportunities if llvm_lto and shared.Building.can_use_unsafe_opts(): if not shared.Building.can_inline(): link_opts.append('-disable-inlining') - link_opts.append('-std-link-opts') + # do not internalize in std-link-opts - it ignores internalize-public-api-list - and add a manual internalize + link_opts += ['-disable-internalize'] + shared.Building.get_safe_internalize() + ['-std-link-opts'] else: - # At least remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) - link_opts += ['-internalize', '-globaldce'] - if link_opts: + # At minimum remove dead functions etc., this potentially saves a lot in the size of the generated code (and the time to compile it) + link_opts += shared.Building.get_safe_internalize() + ['-globaldce'] if DEBUG: print >> sys.stderr, 'emcc: LLVM linktime:', link_opts shared.Building.llvm_opt(in_temp(target_basename + '.bc'), link_opts) if DEBUG: save_intermediate('linktime', 'bc') @@ -1035,13 +1076,14 @@ try: if AUTODEBUG: if DEBUG: print >> sys.stderr, 'emcc: autodebug' - execute(shared.ENV_PREFIX + ['python', shared.AUTODEBUGGER, final, final + '.ad.ll']) + execute([shared.PYTHON, shared.AUTODEBUGGER, final, final + '.ad.ll']) final += '.ad.ll' if DEBUG: save_intermediate('autodebug', 'll') # Emscripten if DEBUG: print >> sys.stderr, 'emcc: LLVM => JS' extra_args = [] if not js_libraries else ['--libraries', ','.join(map(os.path.abspath, js_libraries))] + if jcache: extra_args.append('--jcache') final = shared.Building.emscripten(final, append_ext=False, extra_args=extra_args) if DEBUG: save_intermediate('original') @@ -1057,7 +1099,7 @@ try: file_args += embed_files if Compression.on: file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name] - code = execute(shared.ENV_PREFIX + ['python', shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0] + code = execute([shared.PYTHON, shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0] src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\n' + code) final += '.files.js' open(final, 'w').write(src) @@ -1098,12 +1140,12 @@ try: if len(js_optimizer_queue) > 0: if DEBUG < 2: if DEBUG: print >> sys.stderr, 'emcc: applying js optimization passes:', js_optimizer_queue - final = shared.Building.js_optimizer(final, js_optimizer_queue) + final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache) if DEBUG: save_intermediate('js_opts') else: for name in js_optimizer_queue: print >> sys.stderr, 'emcc: applying js optimization pass:', name - final = shared.Building.js_optimizer(final, [name]) + final = shared.Building.js_optimizer(final, [name], jcache) save_intermediate(name) js_optimizer_queue = [] @@ -1112,7 +1154,7 @@ try: if DEBUG >= 2: # Clean up the syntax a bit - final = shared.Building.js_optimizer(final, []) + final = shared.Building.js_optimizer(final, [], jcache) if DEBUG: save_intermediate('pretty') def get_eliminate(): @@ -1219,6 +1261,8 @@ try: # copy final JS to output shutil.move(final, target) + if DEBUG: print >> sys.stderr, 'emcc: total time: %.2f seconds' % (time.time() - start_time) + finally: if not TEMP_DIR: try: diff --git a/emconfigure b/emconfigure index 64ca8c4fa891c..51e57c64ae097 100755 --- a/emconfigure +++ b/emconfigure @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' This is a helper script. It runs ./configure for you, setting diff --git a/emlibtool b/emlibtool index 1220b93d88e67..1eb18edcf27dc 100755 --- a/emlibtool +++ b/emlibtool @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' This is a helper script. See emcc. diff --git a/emmake b/emmake index 70e415a31bc14..05e0a59765285 100755 --- a/emmake +++ b/emmake @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' This is a helper script. It runs make for you, setting diff --git a/emranlib b/emranlib index 1405320b32b6a..35cb44c950d91 100755 --- a/emranlib +++ b/emranlib @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' emcc - ranlib helper script diff --git a/emscripten.py b/emscripten.py index e7321903d5dcf..3c636447ae535 100755 --- a/emscripten.py +++ b/emscripten.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' You should normally never use this! Use emcc instead. @@ -32,6 +32,7 @@ def path_from_root(*pathelems): temp_files = shared.TempFiles() compiler_engine = None +jcache = False def scan(ll, settings): # blockaddress(@main, %23) @@ -42,16 +43,18 @@ def scan(ll, settings): if len(blockaddrs) > 0: settings['NECESSARY_BLOCKADDRS'] = blockaddrs -NUM_CHUNKS_PER_CORE = 4 +NUM_CHUNKS_PER_CORE = 5 MIN_CHUNK_SIZE = 1024*1024 MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes def process_funcs(args): - i, ll, settings_file, compiler, forwarded_file, libraries = args + i, funcs, meta, settings_file, compiler, forwarded_file, libraries = args + ll = ''.join(funcs) + '\n' + meta funcs_file = temp_files.get('.func_%d.ll' % i).name open(funcs_file, 'w').write(ll) out = shared.run_js(compiler, compiler_engine, [settings_file, funcs_file, 'funcs', forwarded_file] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src')) - return out.split('//FORWARDED_DATA:') + shared.try_delete(funcs_file) + return out def emscript(infile, settings, outfile, libraries=[]): """Runs the emscripten LLVM-to-JS compiler. We parallelize as much as possible @@ -72,6 +75,8 @@ def emscript(infile, settings, outfile, libraries=[]): if DEBUG: print >> sys.stderr, 'emscript: ll=>js' + if jcache: shared.JCache.ensure() + # Pre-scan ll and alter settings as necessary if DEBUG: t = time.time() ll = open(infile).read() @@ -83,31 +88,32 @@ def emscript(infile, settings, outfile, libraries=[]): # Split input into the relevant parts for each phase pre = [] funcs = [] # split up functions here, for parallelism later + func_idents = [] meta = [] # needed by each function XXX - post = [] if DEBUG: t = time.time() in_func = False ll_lines = open(infile).readlines() for line in ll_lines: if in_func: - funcs[-1].append(line) + funcs[-1][1].append(line) if line.startswith('}'): in_func = False - funcs[-1] = ''.join(funcs[-1]) + funcs[-1] = (funcs[-1][0], ''.join(funcs[-1][1])) pre.append(line) # pre needs it to, so we know about all implemented functions else: + if line.startswith(';'): continue if line.startswith('define '): in_func = True - funcs.append([line]) + funcs.append((line, [line])) # use the entire line as the identifier pre.append(line) # pre needs it to, so we know about all implemented functions elif line.find(' = type { ') > 0: pre.append(line) # type elif line.startswith('!'): + if line.startswith('!llvm.module'): continue # we can ignore that meta.append(line) # metadata else: - post.append(line) # global - pre.append(line) # pre needs it to, so we know about globals in pre and funcs + pre.append(line) # pre needs it so we know about globals in pre and funcs. So emit globals there ll_lines = None meta = ''.join(meta) if DEBUG and len(meta) > 1024*1024: print >> sys.stderr, 'emscript warning: large amounts of metadata, will slow things down' @@ -119,24 +125,32 @@ def emscript(infile, settings, outfile, libraries=[]): # print >> sys.stderr, '========== funcs ===============\n' # for func in funcs: # print >> sys.stderr, '\n// ===\n\n', ''.join(func) - # print >> sys.stderr, '========== post ==============\n' - # print >> sys.stderr, ''.join(post) # print >> sys.stderr, '=========================\n' # Save settings to a file to work around v8 issue 1579 settings_file = temp_files.get('.txt').name + settings_text = json.dumps(settings) s = open(settings_file, 'w') - s.write(json.dumps(settings)) + s.write(settings_text) s.close() # Phase 1 - pre if DEBUG: t = time.time() pre_file = temp_files.get('.pre.ll').name - open(pre_file, 'w').write(''.join(pre) + '\n' + meta) - out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src')) - js, forwarded_data = out.split('//FORWARDED_DATA:') - #print 'js', js - #print >> sys.stderr, 'FORWARDED_DATA 1:', forwarded_data, type(forwarded_data) + pre_input = ''.join(pre) + '\n' + meta + out = None + if jcache: + keys = [pre_input, settings_text, ','.join(libraries)] + shortkey = shared.JCache.get_shortkey(keys) + out = shared.JCache.get(shortkey, keys) + if out and DEBUG: print >> sys.stderr, ' loading pre from jcache' + if not out: + open(pre_file, 'w').write(pre_input) + out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, pre_file, 'pre'] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src')) + if jcache: + if DEBUG: print >> sys.stderr, ' saving pre to jcache' + shared.JCache.set(shortkey, keys, out) + pre, forwarded_data = out.split('//FORWARDED_DATA:') forwarded_file = temp_files.get('.json').name open(forwarded_file, 'w').write(forwarded_data) if DEBUG: print >> sys.stderr, ' emscript: phase 1 took %s seconds' % (time.time() - t) @@ -148,7 +162,7 @@ def emscript(infile, settings, outfile, libraries=[]): if cores > 1: intended_num_chunks = cores * NUM_CHUNKS_PER_CORE chunk_size = max(MIN_CHUNK_SIZE, total_ll_size / intended_num_chunks) - chunk_size += len(forwarded_data)/3 + 3*len(meta) # keep ratio of lots of function code to meta (expensive to process) and forwarded (cheap) + chunk_size += 3*len(meta) # keep ratio of lots of function code to meta (expensive to process, and done in each parallel task) chunk_size = min(MAX_CHUNK_SIZE, chunk_size) else: chunk_size = MAX_CHUNK_SIZE # if 1 core, just use the max chunk size @@ -156,31 +170,62 @@ def emscript(infile, settings, outfile, libraries=[]): if DEBUG: t = time.time() forwarded_json = json.loads(forwarded_data) indexed_functions = set() - chunks = [] # bundles of functions - curr = '' - for i in range(len(funcs)): - func = funcs[i] - if len(curr) + len(func) < chunk_size: - curr += func + + chunks = shared.JCache.chunkify(funcs, chunk_size, 'emscript_files' if jcache else None) + + if jcache: + # load chunks from cache where we can # TODO: ignore small chunks + cached_outputs = [] + def load_from_cache(chunk): + keys = [settings_text, forwarded_data, chunk] + shortkey = shared.JCache.get_shortkey(keys) # TODO: share shortkeys with later code + out = shared.JCache.get(shortkey, keys) # this is relatively expensive (pickling?) + if out: + cached_outputs.append(out) + return False + return True + chunks = filter(load_from_cache, chunks) + if len(cached_outputs) > 0: + if out and DEBUG: print >> sys.stderr, ' loading %d funcchunks from jcache' % len(cached_outputs) else: - chunks.append(curr) - curr = func - if curr: - chunks.append(curr) - curr = '' + cached_outputs = [] + + # TODO: minimize size of forwarded data from funcs to what we actually need + if cores == 1 and total_ll_size < MAX_CHUNK_SIZE: assert len(chunks) == 1, 'no point in splitting up without multiple cores' - if DEBUG: print >> sys.stderr, ' emscript: phase 2 working on %d chunks %s (intended chunk size: %.2f MB, meta: %.2f MB, forwarded: %.2f)' % (len(chunks), ('using %d cores' % cores) if len(chunks) > 1 else '', chunk_size/(1024*1024.), len(meta)/(1024*1024.), len(forwarded_data)/(1024*1024.)) - commands = [(i, chunks[i] + '\n' + meta, settings_file, compiler, forwarded_file, libraries) for i in range(len(chunks))] + if len(chunks) > 0: + if DEBUG: print >> sys.stderr, ' emscript: phase 2 working on %d chunks %s (intended chunk size: %.2f MB, meta: %.2f MB, forwarded: %.2f MB, total: %.2f MB)' % (len(chunks), ('using %d cores' % cores) if len(chunks) > 1 else '', chunk_size/(1024*1024.), len(meta)/(1024*1024.), len(forwarded_data)/(1024*1024.), total_ll_size/(1024*1024.)) + + commands = [(i, chunks[i], meta, settings_file, compiler, forwarded_file, libraries) for i in range(len(chunks))] - if len(chunks) > 1: - pool = multiprocessing.Pool(processes=cores) - outputs = pool.map(process_funcs, commands, chunksize=1) + if len(chunks) > 1: + pool = multiprocessing.Pool(processes=cores) + outputs = pool.map(process_funcs, commands, chunksize=1) + elif len(chunks) == 1: + outputs = [process_funcs(commands[0])] else: - outputs = [process_funcs(commands[0])] + outputs = [] + + if jcache: + # save chunks to cache + for i in range(len(chunks)): + chunk = chunks[i] + keys = [settings_text, forwarded_data, chunk] + shortkey = shared.JCache.get_shortkey(keys) + shared.JCache.set(shortkey, keys, outputs[i]) + if out and DEBUG and len(chunks) > 0: print >> sys.stderr, ' saving %d funcchunks to jcache' % len(chunks) + + if jcache: outputs += cached_outputs # TODO: preserve order + + outputs = [output.split('//FORWARDED_DATA:') for output in outputs] + + if DEBUG: print >> sys.stderr, ' emscript: phase 2 took %s seconds' % (time.time() - t) + if DEBUG: t = time.time() - for funcs_js, curr_forwarded_data in outputs: - js += funcs_js + funcs_js = ''.join([output[0] for output in outputs]) + + for func_js, curr_forwarded_data in outputs: # merge forwarded data curr_forwarded_json = json.loads(curr_forwarded_data) forwarded_json['Types']['preciseI64MathUsed'] = forwarded_json['Types']['preciseI64MathUsed'] or curr_forwarded_json['Types']['preciseI64MathUsed'] @@ -188,7 +233,8 @@ def emscript(infile, settings, outfile, libraries=[]): forwarded_json['Functions']['blockAddresses'][key] = value for key in curr_forwarded_json['Functions']['indexedFunctions'].iterkeys(): indexed_functions.add(key) - if DEBUG: print >> sys.stderr, ' emscript: phase 2 took %s seconds' % (time.time() - t) + outputs = None + if DEBUG: print >> sys.stderr, ' emscript: phase 2b took %s seconds' % (time.time() - t) if DEBUG: t = time.time() # calculations on merged forwarded data @@ -198,25 +244,38 @@ def emscript(infile, settings, outfile, libraries=[]): forwarded_json['Functions']['indexedFunctions'][indexed] = i # make sure not to modify this python object later - we use it in indexize i += 2 forwarded_json['Functions']['nextIndex'] = i + indexing = forwarded_json['Functions']['indexedFunctions'] def indexize(js): return re.sub(r'{{{ FI_([\w\d_$]+) }}}', lambda m: str(indexing[m.groups(0)[0]]), js) + + blockaddrs = forwarded_json['Functions']['blockAddresses'] + def blockaddrsize(js): + return re.sub(r'{{{ BA_([\w\d_$]+)\|([\w\d_$]+) }}}', lambda m: str(blockaddrs[m.groups(0)[0]][m.groups(0)[1]]), js) + + #if DEBUG: outfile.write('// pre\n') + outfile.write(blockaddrsize(indexize(pre))) + pre = None + + #if DEBUG: outfile.write('// funcs\n') + outfile.write(blockaddrsize(indexize(funcs_js))) + funcs_js = None + # forward forwarded_data = json.dumps(forwarded_json) forwarded_file = temp_files.get('.2.json').name - open(forwarded_file, 'w').write(forwarded_data) - if DEBUG: print >> sys.stderr, ' emscript: phase 2b took %s seconds' % (time.time() - t) + open(forwarded_file, 'w').write(indexize(forwarded_data)) + if DEBUG: print >> sys.stderr, ' emscript: phase 2c took %s seconds' % (time.time() - t) # Phase 3 - post if DEBUG: t = time.time() post_file = temp_files.get('.post.ll').name - open(post_file, 'w').write(''.join(post) + '\n' + meta) + open(post_file, 'w').write('\n') # no input, just processing of forwarded data out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src')) - js += out - js = indexize(js) + #if DEBUG: outfile.write('// post\n') + outfile.write(indexize(out)) if DEBUG: print >> sys.stderr, ' emscript: phase 3 took %s seconds' % (time.time() - t) - outfile.write(js) # TODO: write in parts (see previous line though) outfile.close() @@ -325,6 +384,10 @@ def lookup(value): metavar='FOO=BAR', help=('Overrides for settings defined in settings.js. ' 'May occur multiple times.')) + parser.add_option('-j', '--jcache', + action='store_true', + default=False, + help=('Enable jcache (ccache-like caching of compilation results, for faster incremental builds).')) # Convert to the same format that argparse would have produced. keywords, positional = parser.parse_args() @@ -334,6 +397,7 @@ def lookup(value): if isinstance(keywords.outfile, basestring): keywords.outfile = open(keywords.outfile, 'w') compiler_engine = keywords.compiler + jcache = keywords.jcache temp_files.run_and_clean(lambda: main(keywords)) diff --git a/src/analyzer.js b/src/analyzer.js index fa8aee59d4a67..014579f4498d3 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -467,6 +467,12 @@ function analyzer(data, sidePass) { break; } case 'bitcast': { + if (!sourceBits) { + // we can be asked to bitcast doubles or such to integers, handle that as best we can (if it's a double that + // was an x86_fp80, this code will likely break when called) + sourceBits = targetBits = Runtime.getNativeTypeSize(value.params[0].type); + warn('legalizing non-integer bitcast on ll #' + item.lineNum); + } break; } case 'select': { @@ -684,6 +690,8 @@ function analyzer(data, sidePass) { var subType = check[2]; addTypeInternal(subType, data); // needed for anonymous structure definitions (see below) + // Huge structural types are represented very inefficiently, both here and in generated JS. Best to avoid them - for example static char x[10*1024*1024]; is bad, while static char *x = malloc(10*1024*1024) is fine. + if (num >= 10*1024*1024) warnOnce('warning: very large fixed-size structural type: ' + type + ' - can you reduce it? (compilation may be slow)'); Types.types[nonPointing] = { name_: nonPointing, fields: range(num).map(function() { return subType }), diff --git a/tools/reproduceriter_shell.js b/src/headless.js similarity index 99% rename from tools/reproduceriter_shell.js rename to src/headless.js index f425df82a8d67..a528e8af7077a 100644 --- a/tools/reproduceriter_shell.js +++ b/src/headless.js @@ -1,4 +1,6 @@ +//== HEADLESS ==// + var window = { location: { toString: function() { @@ -666,6 +668,11 @@ var document = { data: new Uint8ClampedArray(w*h), }; }, + save: function(){}, + restore: function(){}, + fillRect: function(){}, + measureText: function() { return 10 }, + fillText: function(){}, }; } default: throw 'canvas.getContext: ' + which; @@ -869,3 +876,10 @@ var MozBlobBuilder = function() { }; }; +// additional setup +if (!Module['canvas']) { + Module['canvas'] = document.getElementById('canvas'); +} + +//== HEADLESS ==// + diff --git a/src/intertyper.js b/src/intertyper.js index fbad353a7fef2..1d18c3cfe9925 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -73,13 +73,11 @@ function intertyper(data, sidePass, baseLineNums) { if (!testType) { var global = /([@%\w\d\.\" $-]+) = .*/.exec(line); var globalIdent = toNiceIdent(global[1]); - var testAlias = /[@%\w\d\.\" $-]+ = alias .*/.exec(line); - var testString = /[@%\w\d\.\" $-]+ = [\w ]+ \[\d+ x i8] c".*/.exec(line); + var testAlias = /[@%\w\d\.\" $-]+ = (hidden )?alias .*/.exec(line); Variables.globals[globalIdent] = { name: globalIdent, alias: !!testAlias, - impl: VAR_EMULATED, - isString : !!testString + impl: VAR_EMULATED }; unparsedGlobals.lines.push(line); } else { @@ -165,7 +163,7 @@ function intertyper(data, sidePass, baseLineNums) { function makeToken(text) { if (text.length == 0) return; // merge certain tokens - if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.exec(text) ) ) { + if (lastToken && ( (lastToken.text == '%' && text[0] == '"') || /^\**$/.test(text) ) ) { lastToken.text += text; return; } @@ -182,7 +180,7 @@ function intertyper(data, sidePass, baseLineNums) { // merge certain tokens if (lastToken && isType(lastToken.text) && isFunctionDef(token)) { lastToken.text += ' ' + text; - } else if (lastToken && /^}\**$/.exec(text)) { // }, }*, etc. + } else if (lastToken && text[0] == '}') { // }, }*, etc. var openBrace = tokens.length-1; while (tokens[openBrace].text.substr(-1) != '{') openBrace --; token = combineTokens(tokens.slice(openBrace+1)); @@ -459,6 +457,9 @@ function intertyper(data, sidePass, baseLineNums) { }; ret.type = ret.value.type; Types.needAnalysis[ret.type] = 0; + if (!NAMED_GLOBALS) { + Variables.globals[ret.ident].type = ret.type; + } return [ret]; } if (item.tokens[2].text == 'type') { @@ -509,6 +510,10 @@ function intertyper(data, sidePass, baseLineNums) { private_: private_, lineNum: item.lineNum }; + if (!NAMED_GLOBALS) { + Variables.globals[ret.ident].type = ret.type; + Variables.globals[ret.ident].external = external; + } Types.needAnalysis[ret.type] = 0; if (ident == '@llvm.global_ctors') { ret.ctors = []; @@ -674,7 +679,7 @@ function intertyper(data, sidePass, baseLineNums) { // 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); + item.ident = tokensLeft[0].text.substr(1, tokensLeft[0].text.length-2) || ';'; // use ; for empty inline assembly return { forward: null, ret: [item], item: item }; } if (item.ident.substr(-2) == '()') { @@ -914,6 +919,7 @@ function intertyper(data, sidePass, baseLineNums) { processItem: function(item) { return [{ intertype: 'resume', + ident: toNiceIdent(item.tokens[2].text), lineNum: item.lineNum }]; } diff --git a/src/jsifier.js b/src/jsifier.js index 9c022efb32876..4192cd3f68566 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -65,24 +65,27 @@ function JSify(data, functionsOnly, givenFunctions) { // Add additional necessary items for the main pass. We can now do this since types are parsed (types can be used through // generateStructInfo in library.js) LibraryManager.load(); - var libFuncsToInclude; - if (INCLUDE_FULL_LIBRARY) { - assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') - libFuncsToInclude = []; - for (var key in LibraryManager.library) { - if (!key.match(/__(deps|postset|inline)$/)) { - libFuncsToInclude.push(key); + + if (phase == 'pre') { + var libFuncsToInclude; + if (INCLUDE_FULL_LIBRARY) { + assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') + libFuncsToInclude = []; + for (var key in LibraryManager.library) { + if (!key.match(/__(deps|postset|inline)$/)) { + libFuncsToInclude.push(key); + } } + } else { + libFuncsToInclude = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE; } - } else { - libFuncsToInclude = DEFAULT_LIBRARY_FUNCS_TO_INCLUDE; - } - libFuncsToInclude.forEach(function(ident) { - data.functionStubs.push({ - intertype: 'functionStub', - ident: '_' + ident + libFuncsToInclude.forEach(function(ident) { + data.functionStubs.push({ + intertype: 'functionStub', + ident: '_' + ident + }); }); - }); + } } // Functions @@ -214,7 +217,7 @@ function JSify(data, functionsOnly, givenFunctions) { throw 'Invalid segment: ' + dump(segment); } assert(segment.type, 'Missing type for constant segment!'); - return indexizeFunctions(ret, segment.type); + return makeGlobalUse(indexizeFunctions(ret, segment.type)); }; return tokens.map(handleSegment) } @@ -223,7 +226,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (value.intertype in PARSABLE_LLVM_FUNCTIONS) { return [finalizeLLVMFunctionCall(value)]; } else if (Runtime.isNumberType(type) || pointingLevels(type) >= 1) { - return indexizeFunctions(parseNumerical(value.value), type); + return makeGlobalUse(indexizeFunctions(parseNumerical(value.value), type)); } else if (value.intertype === 'emptystruct') { return makeEmptyStruct(type); } else if (value.intertype === 'string') { @@ -247,7 +250,7 @@ function JSify(data, functionsOnly, givenFunctions) { processItem: function(item) { function needsPostSet(value) { return value[0] in UNDERSCORE_OPENPARENS || value.substr(0, 14) === 'CHECK_OVERFLOW' - || value.substr(0, 13) === 'STRING_TABLE.'; + || value.substr(0, 6) === 'GLOBAL'; } item.intertype = 'GlobalVariableStub'; @@ -259,16 +262,17 @@ function JSify(data, functionsOnly, givenFunctions) { '\n]);\n'; return ret; } else { + var constant = null; + var allocator = (BUILD_AS_SHARED_LIB && !item.external) ? 'ALLOC_NORMAL' : 'ALLOC_STATIC'; + var index = null; if (item.external && BUILD_AS_SHARED_LIB) { // External variables in shared libraries should not be declared as // they would shadow similarly-named globals in the parent. item.JS = ''; } else { - if (!(item.ident in Variables.globals) || !Variables.globals[item.ident].isString) { - item.JS = 'var ' + item.ident + ';'; - } + item.JS = makeGlobalDef(item.ident); } - var constant = null; + if (item.external) { // Import external global variables from the library if available. var shortident = item.ident.slice(1); @@ -283,7 +287,7 @@ function JSify(data, functionsOnly, givenFunctions) { padding = makeEmptyStruct(item.type); } var padded = val.concat(padding.slice(val.length)); - var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, 'ALLOC_STATIC', item.type) + ';' + var js = item.ident + '=' + makePointer(JSON.stringify(padded), null, allocator, item.type, index) + ';' if (LibraryManager.library[shortident + '__postset']) { js += '\n' + LibraryManager.library[shortident + '__postset']; } @@ -294,6 +298,10 @@ function JSify(data, functionsOnly, givenFunctions) { } return ret; } else { + if (!NAMED_GLOBALS && isIndexableGlobal(item.ident)) { + index = makeGlobalUse(item.ident); // index !== null indicates we are indexing this + allocator = 'ALLOC_NONE'; + } constant = parseConst(item.value, item.type, item.ident); if (typeof constant === 'string' && constant[0] != '[') { constant = [constant]; // A single item. We may need a postset for it. @@ -304,7 +312,7 @@ function JSify(data, functionsOnly, givenFunctions) { if (needsPostSet(value)) { // ident, or expression containing an ident ret.push({ intertype: 'GlobalVariablePostSet', - JS: makeSetValue(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, 'i32', false, true) + ';' // ignore=true, since e.g. rtti and statics cause lots of safe_heap errors }); constant[i] = '0'; } @@ -313,23 +321,19 @@ function JSify(data, functionsOnly, givenFunctions) { } // NOTE: This is the only place that could potentially create static // allocations in a shared library. - constant = makePointer(constant, null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', item.type); - + constant = makePointer(constant, null, allocator, item.type, index); var js; - // Strings are held in STRING_TABLE, to not clutter up the main namespace (in some cases we have - // many many strings, possibly exceeding the js engine limit on global vars). - if (Variables.globals[item.ident].isString) { - js = 'STRING_TABLE.' + item.ident + '=' + constant + ';'; - } else { - js = item.ident + '=' + constant + ';'; - } + js = (index !== null ? '' : item.ident + '=') + constant + ';'; // \n Module.print("' + item.ident + ' :" + ' + makeGlobalUse(item.ident) + ');'; // Special case: class vtables. We make sure they are null-terminated, to allow easy runtime operations if (item.ident.substr(0, 5) == '__ZTV') { - js += '\n' + makePointer('[0]', null, BUILD_AS_SHARED_LIB ? 'ALLOC_NORMAL' : 'ALLOC_STATIC', ['void*']) + ';'; + if (index !== null) { + index = getFastValue(index, '+', Runtime.alignMemory(calcAllocatedSize(Variables.globals[item.ident].type))); + } + js += '\n' + makePointer('[0]', null, allocator, ['void*'], index) + ';'; } - if (item.ident in EXPORTED_GLOBALS) { + if (EXPORT_ALL || (item.ident in EXPORTED_GLOBALS)) { js += '\nModule["' + item.ident + '"] = ' + item.ident + ';'; } if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { @@ -439,7 +443,7 @@ function JSify(data, functionsOnly, givenFunctions) { } var text = (deps ? '\n' + deps.map(addFromLibrary).filter(function(x) { return x != '' }).join('\n') : ''); text += isFunction ? snippet : 'var ' + ident + '=' + snippet + ';'; - if (ident in EXPORTED_FUNCTIONS) { + if (EXPORT_ALL || (ident in EXPORTED_FUNCTIONS)) { text += '\nModule["' + ident + '"] = ' + ident + ';'; } return text; @@ -482,6 +486,19 @@ function JSify(data, functionsOnly, givenFunctions) { } }); + // function for filtering functions for label debugging + if (LABEL_FUNCTION_FILTERS.length > 0) { + var LABEL_FUNCTION_FILTER_SET = set(LABEL_FUNCTION_FILTERS); + var functionNameFilterTest = function(ident) { + return (ident in LABEL_FUNCTION_FILTER_SET); + }; + } else { + // no filters are specified, all function names are printed + var functionNameFilterTest = function(ident) { + return true; + } + } + // function reconstructor & post-JS optimizer substrate.addActor('FunctionReconstructor', { funcs: {}, @@ -562,7 +579,7 @@ function JSify(data, functionsOnly, givenFunctions) { } }); - if (LABEL_DEBUG) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n"; + if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " Module.print(INDENT + ' Entering: " + func.ident + ": ' + Array.prototype.slice.call(arguments)); INDENT += ' ';\n"; if (true) { // TODO: optimize away when not needed if (CLOSURE_ANNOTATIONS) func.JS += '/** @type {number} */'; @@ -576,7 +593,7 @@ function JSify(data, functionsOnly, givenFunctions) { function getLabelLines(label, indent, relooping) { if (!label) return ''; var ret = ''; - if (LABEL_DEBUG >= 2) { + if ((LABEL_DEBUG >= 2) && functionNameFilterTest(func.ident)) { ret += indent + "Module.print(INDENT + '" + func.ident + ":" + label.ident + "');\n"; } if (EXECUTION_TIMEOUT > 0) { @@ -616,6 +633,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // otherwise, should have been set before! if (func.setjmpTable) { var setjmpTable = {}; + ret += indent + 'var mySetjmpIds = {};\n'; ret += indent + 'var setjmpTable = {'; func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },'; @@ -634,7 +652,7 @@ function JSify(data, functionsOnly, givenFunctions) { }).join('\n'); ret += '\n' + indent + ' default: assert(0, "bad label: " + label);\n' + indent + '}'; if (func.setjmpTable) { - ret += ' } catch(e) { if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }'; + ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }'; } } else { ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent); @@ -687,7 +705,7 @@ function JSify(data, functionsOnly, givenFunctions) { } func.JS += walkBlock(func.block, ' '); // Finalize function - if (LABEL_DEBUG) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n"; + if (LABEL_DEBUG && functionNameFilterTest(func.ident)) func.JS += " INDENT = INDENT.substr(0, INDENT.length-2);\n"; // Add an unneeded return, needed for strict mode to not throw warnings in some cases. // If we are not relooping, then switches make it unimportant to have this (and, we lack hasReturn anyhow) if (RELOOP && func.lines.length > 0 && func.labels.filter(function(label) { return label.hasReturn }).length > 0) { @@ -699,7 +717,7 @@ function JSify(data, functionsOnly, givenFunctions) { func.JS += '\n//FUNCTION_END_MARKER_OF_SOURCE_FILE_' + associatedSourceFile + '\n'; } - if (func.ident in EXPORTED_FUNCTIONS) { + if (EXPORT_ALL || (func.ident in EXPORTED_FUNCTIONS)) { func.JS += 'Module["' + func.ident + '"] = ' + func.ident + ';'; } @@ -817,7 +835,7 @@ function JSify(data, functionsOnly, givenFunctions) { break; case VAR_EMULATED: if (item.pointer.intertype == 'value') { - return makeSetValue(item.ident, 0, value, item.valueType, 0, 0, item.align) + ';'; + return makeSetValue(makeGlobalUse(item.ident), 0, value, item.valueType, 0, 0, item.align) + ';'; } else { return makeSetValue(0, finalizeLLVMParameter(item.pointer), value, item.valueType, 0, 0, item.align) + ';'; } @@ -1020,7 +1038,7 @@ function JSify(data, functionsOnly, givenFunctions) { + 'PROFILING_NODE = __parentProfilingNode__ ' + '}\n'; } - if (LABEL_DEBUG) { + if (LABEL_DEBUG && functionNameFilterTest(item.funcData.ident)) { ret += "Module.print(INDENT + 'Exiting: " + item.funcData.ident + "');\n" + "INDENT = INDENT.substr(0, INDENT.length-2);\n"; } @@ -1031,8 +1049,10 @@ function JSify(data, functionsOnly, givenFunctions) { return ret + ';'; }); makeFuncLineActor('resume', function(item) { + // If there is no current exception, set this one as it (during a resume, the current exception can be wiped out) return (EXCEPTION_DEBUG ? 'Module.print("Resuming exception");' : '') + - 'throw ' + makeGetValue('_llvm_eh_exception.buf', '0', 'void*') + ';'; + 'if (' + makeGetValue('_llvm_eh_exception.buf', 0, 'void*') + ' == 0) { ' + makeSetValue('_llvm_eh_exception.buf', 0, item.ident + '.f0', 'void*') + ' } ' + + 'throw ' + item.ident + '.f0;'; }); makeFuncLineActor('invoke', function(item) { // Wrapping in a function lets us easily return values if we are @@ -1066,12 +1086,12 @@ function JSify(data, functionsOnly, givenFunctions) { var param1 = finalizeLLVMParameter(item.params[0]); var param2 = finalizeLLVMParameter(item.params[1]); switch (item.op) { - case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type) + ',tempValue)'; - case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type) + ',tempValue)'; - case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type) + ',tempValue)'; + case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type, null, null, null, null, ',') + ',tempValue)'; + case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type, null, null, null, null, ',') + ',tempValue)'; case 'cmpxchg': { var param3 = finalizeLLVMParameter(item.params[2]); - return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==' + param2 + ' && (' + makeSetValue(param1, 0, param3, type) + ')),tempValue)'; + return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==' + param2 + ' && (' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ')),tempValue)'; } default: throw 'unhandled atomic op: ' + item.op; } @@ -1148,6 +1168,7 @@ function JSify(data, functionsOnly, givenFunctions) { var argsTypes = []; var varargs = []; var varargsTypes = []; + var varargsByVals = {}; var ignoreFunctionIndexizing = []; var useJSArgs = (shortident + '__jsargs') in LibraryManager.library; var hasVarArgs = isVarArgsFunctionType(type); @@ -1159,10 +1180,18 @@ function JSify(data, functionsOnly, givenFunctions) { args.push(val); argsTypes.push(param.type); } else { + var size; + if (param.byVal) { + varargsByVals[varargs.length] = param.byVal; + size = calcAllocatedSize(removeAllPointing(param.type)); + } else { + size = Runtime.getNativeFieldSize(param.type); + } varargs.push(val); - varargs = varargs.concat(zeros(Runtime.getNativeFieldSize(param.type)-1)); + varargs = varargs.concat(zeros(size-1)); + // TODO: replace concats like this with push varargsTypes.push(param.type); - varargsTypes = varargsTypes.concat(zeros(Runtime.getNativeFieldSize(param.type)-1)); + varargsTypes = varargsTypes.concat(zeros(size-1)); } }); @@ -1182,8 +1211,17 @@ function JSify(data, functionsOnly, givenFunctions) { varargs.map(function(arg, i) { var type = varargsTypes[i]; if (type == 0) return null; - var ret = makeSetValue(getFastValue('tempInt', '+', offset), 0, arg, type, null, null, QUANTUM_SIZE, null, ','); - offset += Runtime.getNativeFieldSize(type); + var ret; + if (!varargsByVals[i]) { + ret = makeSetValue(getFastValue('tempInt', '+', offset), 0, arg, type, null, null, QUANTUM_SIZE, null, ','); + offset += Runtime.getNativeFieldSize(type); + } else { + assert(offset % 4 == 0); // varargs must be aligned + var size = calcAllocatedSize(removeAllPointing(type)); + assert(size % 4 == 0); // varargs must stay aligned + ret = makeCopyValues(getFastValue('tempInt', '+', offset), arg, size, null, null, varargsByVals[i], ','); + offset += size; + } return ret; }).filter(function(arg) { return arg !== null; @@ -1256,11 +1294,49 @@ function JSify(data, functionsOnly, givenFunctions) { // if (!mainPass) { + if (phase == 'pre' && !Variables.generatedGlobalBase) { + Variables.generatedGlobalBase = true; + if (Variables.nextIndexedOffset > 0) { + // Variables have been calculated, print out the base generation before we print them + print('var GLOBAL_BASE = STATICTOP;\n'); + print('STATICTOP += ' + Variables.nextIndexedOffset + ';\n'); + print('assert(STATICTOP < TOTAL_MEMORY);\n'); + } + } var generated = itemsDict.function.concat(itemsDict.type).concat(itemsDict.GlobalVariableStub).concat(itemsDict.GlobalVariable).concat(itemsDict.GlobalVariablePostSet); if (!DEBUG_MEMORY) print(generated.map(function(item) { return item.JS }).join('\n')); return; } + // Print out global variables and postsets TODO: batching + if (phase == 'pre') { + legalizedI64s = false; + + var globalsData = analyzer(intertyper(data.unparsedGlobalss[0].lines, true), true); + + if (!NAMED_GLOBALS) { + sortGlobals(globalsData.globalVariables).forEach(function(g) { + var ident = g.ident; + if (!isIndexableGlobal(ident)) return; + Variables.indexedGlobals[ident] = Variables.nextIndexedOffset; + Variables.nextIndexedOffset += Runtime.alignMemory(calcAllocatedSize(Variables.globals[ident].type)); + if (ident.substr(0, 5) == '__ZTV') { // leave room for null-terminating the vtable + Variables.nextIndexedOffset += Runtime.getNativeTypeSize('i32'); + } + }); + } + + JSify(globalsData, true, Functions); + globalsData = null; + data.unparsedGlobalss = null; + + var generated = itemsDict.functionStub.concat(itemsDict.GlobalVariablePostSet); + generated.forEach(function(item) { print(indentify(item.JS || '', 2)); }); + } else { + assert(data.unparsedGlobalss[0].lines.length == 0, dump([phase, data.unparsedGlobalss])); + assert(itemsDict.functionStub.length == 0, dump([phase, itemsDict.functionStub])); + } + if (phase == 'pre' || phase == 'funcs') { // serialize out the data that later passes need PassManager.serialize(); // XXX for funcs pass, do not serialize it all. I think we just need which were indexized. @@ -1276,8 +1352,11 @@ function JSify(data, functionsOnly, givenFunctions) { print('// Warning: printing of i64 values may be slightly rounded! No deep i64 math used, so precise i64 code not included'); print('var i64Math = null;'); } - var generated = itemsDict.functionStub.concat(itemsDict.GlobalVariablePostSet); - generated.forEach(function(item) { print(indentify(item.JS || '', 2)); }); + if (HEADLESS) { + print('if (!ENVIRONMENT_IS_WEB) {'); + print(read('headless.js').replace("'%s'", "'http://emscripten.org'").replace("'?%s'", "''").replace('%s,', 'null,').replace('%d', '0')); + print('}'); + } if (RUNTIME_TYPE_INFO) { Types.cleanForRuntime(); print('Runtime.typeInfo = ' + JSON.stringify(Types.types)); @@ -1287,11 +1366,6 @@ function JSify(data, functionsOnly, givenFunctions) { var postParts = processMacros(preprocess(read(postFile))).split('{{GLOBAL_VARS}}'); print(postParts[0]); - // Print out global variables and postsets TODO: batching - legalizedI64s = false; - JSify(analyzer(intertyper(data.unparsedGlobalss[0].lines, true), true), true, Functions); - data.unparsedGlobalss = null; - print(Functions.generateIndexing()); // done last, as it may rely on aliases set in postsets // Load runtime-linked libraries @@ -1317,7 +1391,7 @@ function JSify(data, functionsOnly, givenFunctions) { substrate.addItems(data.functionStubs, 'FunctionStub'); assert(data.functions.length == 0); } else { - substrate.addItems(values(data.globalVariables), 'GlobalVariable'); + substrate.addItems(sortGlobals(data.globalVariables), 'GlobalVariable'); substrate.addItems(data.aliass, 'Alias'); substrate.addItems(data.functions, 'FunctionSplitter'); } diff --git a/src/library.js b/src/library.js index fd5e0faed4a13..93d54d682a951 100644 --- a/src/library.js +++ b/src/library.js @@ -2457,20 +2457,23 @@ LibraryManager.library = { var argIndex = 0; var next; - next = 1; mainLoop: - for (var formatIndex = 0; formatIndex < format.length; formatIndex++) { + for (var formatIndex = 0; formatIndex < format.length;) { + if (format[formatIndex] === '%' && format[formatIndex+1] == 'n') { + var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; + argIndex += Runtime.getNativeFieldSize('void*'); + {{{ makeSetValue('argPtr', 0, 'soFar', 'i32') }}}; + formatIndex += 2; + continue; + } + // remove whitespace while (1) { next = get(); if (next == 0) return fields; if (!(next in __scanString.whiteSpace)) break; - } - unget(next); - - if (next <= 0) return fields; - var next = get(); - if (next <= 0) return fields; // End of input. + } + unget(); if (format[formatIndex] === '%') { formatIndex++; @@ -2504,6 +2507,7 @@ LibraryManager.library = { // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later if (type == 'f') { var last = 0; + next = get(); while (next > 0) { buffer.push(String.fromCharCode(next)); if (__isFloat(buffer.join(''))) { @@ -2511,12 +2515,12 @@ LibraryManager.library = { } next = get(); } - unget(next); - while (buffer.length > last) { - unget(buffer.pop().charCodeAt(0)); + for (var i = 0; i < buffer.length - last + 1; i++) { + unget(); } + buffer.length = last; + } else { next = get(); - } else if (type != 'n') { var first = true; while ((curr < max_ || isNaN(max_)) && next > 0) { if (!(next in __scanString.whiteSpace) && // stop on whitespace @@ -2530,13 +2534,14 @@ LibraryManager.library = { buffer.push(String.fromCharCode(next)); next = get(); curr++; + first = false; } else { break; } - first = false; } + unget(); } - if (buffer.length === 0 && type != 'n') return 0; // Failure. + if (buffer.length === 0) return 0; // Failure. var text = buffer.join(''); var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; argIndex += Runtime.getNativeFieldSize('void*'); @@ -2566,31 +2571,26 @@ LibraryManager.library = { {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}} } break; - case 'n': - {{{ makeSetValue('argPtr', 0, 'soFar-1', 'i32') }}} - break; } - if (type != 'n') fields++; - if (next <= 0) break mainLoop; // End of input. + fields++; } else if (format[formatIndex] in __scanString.whiteSpace) { + next = get(); while (next in __scanString.whiteSpace) { - next = get(); if (next <= 0) break mainLoop; // End of input. + next = get(); } unget(next); + formatIndex++; } else { // Not a specifier. + next = get(); if (format[formatIndex].charCodeAt(0) !== next) { unget(next); break mainLoop; } + formatIndex++; } } - // 'n' is special in that it needs no input. so it can be at the end, even with nothing left to read - if (format[formatIndex-1] == '%' && format[formatIndex] == 'n') { - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - {{{ makeSetValue('argPtr', 0, 'soFar-1', 'i32') }}} - } return fields; }, // Performs prtinf-style formatting. @@ -2741,7 +2741,7 @@ LibraryManager.library = { var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0); argSize = argSize || 4; var currArg = getNextArg('i' + (argSize * 8)); -#if PRECISE_I64_MATH == 1 +#if PRECISE_I64_MATH var origArg = currArg; #endif var argText; @@ -2760,12 +2760,12 @@ LibraryManager.library = { var currAbsArg = Math.abs(currArg); var prefix = ''; if (next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0)) { -#if PRECISE_I64_MATH == 1 - if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1]); else +#if PRECISE_I64_MATH + if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], null); else #endif argText = reSign(currArg, 8 * argSize, 1).toString(10); } else if (next == 'u'.charCodeAt(0)) { -#if PRECISE_I64_MATH == 1 +#if PRECISE_I64_MATH if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], true); else #endif argText = unSign(currArg, 8 * argSize, 1).toString(10); @@ -2774,6 +2774,9 @@ LibraryManager.library = { argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8); } else if (next == 'x'.charCodeAt(0) || next == 'X'.charCodeAt(0)) { prefix = flagAlternative ? '0x' : ''; +#if PRECISE_I64_MATH + if (argSize == 8 && i64Math) argText = (origArg[1]>>>0).toString(16) + (origArg[0]>>>0).toString(16); else +#endif if (currArg < 0) { // Represent negative numbers in hex as 2's complement. currArg = -currArg; @@ -3452,8 +3455,9 @@ LibraryManager.library = { // int fscanf(FILE *restrict stream, const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html if (FS.streams[stream]) { - var get = function() { return _fgetc(stream); }; - var unget = function(c) { return _ungetc(c, stream); }; + var stack = []; + var get = function() { var ret = _fgetc(stream); stack.push(ret); return ret }; + var unget = function(c) { return _ungetc(stack.pop(), stream) }; return __scanString(format, get, unget, varargs); } else { return -1; @@ -3481,11 +3485,11 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html var result = __formatString(format, varargs); var limit = (n === undefined) ? result.length - : Math.min(result.length, n - 1); + : Math.min(result.length, Math.max(n - 1, 0)); for (var i = 0; i < limit; i++) { {{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}}; } - {{{ makeSetValue('s', 'i', '0', 'i8') }}}; + if (limit < n || (n === undefined)) {{{ makeSetValue('s', 'i', '0', 'i8') }}}; return result.length; }, fprintf__deps: ['fwrite', '_formatString'], @@ -3735,6 +3739,7 @@ LibraryManager.library = { strtod_l: 'strtod', // no locale support yet strtold: 'strtod', // XXX add real support for long double strtold_l: 'strtold', // no locale support yet + strtof: 'strtod', // use stdtod to handle strtof _parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'], _parseInt: function(str, endptr, base, min, max, bits, unsign) { @@ -4035,6 +4040,10 @@ LibraryManager.library = { return Math.floor(Math.random()*0x80000000); }, + drand48: function() { + return Math.random(); + }, + realpath__deps: ['$FS', '__setErrNo'], realpath: function(file_name, resolved_name) { // char *realpath(const char *restrict file_name, char *restrict resolved_name); @@ -4742,6 +4751,10 @@ LibraryManager.library = { return 32; }, + llvm_trap: function() { + throw 'trap! ' + new Error().stack; + }, + __assert_fail: function(condition, file, line) { ABORT = true; throw 'Assertion failed: ' + Pointer_stringify(condition);//JSON.stringify(arguments)//condition; @@ -5104,6 +5117,34 @@ LibraryManager.library = { llvm_objectsize_i32: function() { return -1 }, // TODO: support this + // ========================================================================== + // llvm-mono integration + // ========================================================================== + + llvm_mono_load_i8_p0i8: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i8') }}}; + }, + + llvm_mono_store_i8_p0i8: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i8') }}}; + }, + + llvm_mono_load_i16_p0i16: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i16') }}}; + }, + + llvm_mono_store_i16_p0i16: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i16') }}}; + }, + + llvm_mono_load_i32_p0i32: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i32') }}}; + }, + + llvm_mono_store_i32_p0i32: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i32') }}}; + }, + // ========================================================================== // math.h // ========================================================================== @@ -5124,6 +5165,64 @@ LibraryManager.library = { atan2f: 'Math.atan2', exp: 'Math.exp', expf: 'Math.exp', + + // The erf and erfc functions are inspired from + // http://www.digitalmars.com/archives/cplusplus/3634.html + // and mruby source code at + // https://github.com/mruby/mruby/blob/master/src/math.c + erfc: function (x) { + var MATH_TOLERANCE = 1E-12; + var ONE_SQRTPI = 0.564189583547756287; + var a = 1; + var b = x; + var c = x; + var d = x * x + 0.5; + var n = 1.0; + var q2 = b / d; + var q1, t; + + if (Math.abs(x) < 2.2) { + return 1.0 - _erf(x); + } + if (x < 0) { + return 2.0 - _erfc(-x); + } + do { + t = a * n + b * x; + a = b; + b = t; + t = c * n + d * x; + c = d; + d = t; + n += 0.5; + q1 = q2; + q2 = b / d; + } while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE); + return (ONE_SQRTPI * Math.exp(- x * x) * q2); + }, + erfcf: 'erfcf', + erf: function (x) { + var MATH_TOLERANCE = 1E-12; + var TWO_SQRTPI = 1.128379167095512574; + var sum = x; + var term = x; + var xsqr = x*x; + var j = 1; + + if (Math.abs(x) > 2.2) { + return 1.0 - _erfc(x); + } + do { + term *= xsqr / j; + sum -= term / (2 * j + 1); + ++j; + term *= xsqr / j; + sum += term / (2 * j + 1); + ++j; + } while (Math.abs(term / sum) > MATH_TOLERANCE); + return (TWO_SQRTPI * sum); + }, + erff: 'erf', log: 'Math.log', logf: 'Math.log', sqrt: 'Math.sqrt', @@ -5266,7 +5365,7 @@ LibraryManager.library = { }, fmaxf: 'fmax', fmin: function(x, y) { - return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y); + return isNaN(x) ? y : isNaN(y) ? x : Math.min(x, y); }, fminf: 'fmin', fma: function(x, y, z) { @@ -5786,6 +5885,26 @@ LibraryManager.library = { return 0; }, + // ========================================================================== + // sys/timeb.h + // ========================================================================== + + __timeb_struct_layout: Runtime.generateStructInfo([ + ['i32', 'time'], + ['i16', 'millitm'], + ['i16', 'timezone'], + ['i16', 'dstflag'] + ]), + ftime__deps: ['__timeb_struct_layout'], + ftime: function(p) { + var millis = Date.now(); + {{{ makeSetValue('p', '___timeb_struct_layout.time', 'Math.floor(millis/1000)', 'i32') }}}; + {{{ makeSetValue('p', '___timeb_struct_layout.millitm', 'millis % 1000', 'i16') }}}; + {{{ makeSetValue('p', '___timeb_struct_layout.timezone', '0', 'i16') }}}; // TODO + {{{ makeSetValue('p', '___timeb_struct_layout.dstflag', '0', 'i16') }}}; // TODO + return 0; + }, + // ========================================================================== // sys/times.h // ========================================================================== @@ -5841,11 +5960,11 @@ LibraryManager.library = { setjmp__inline: function(env) { // Save the label - return '(' + makeSetValue(env, '0', 'label', 'i32') + ', 0)'; + return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32') + ', 0)'; }, longjmp: function(env, value) { - throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; + throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; }, // ========================================================================== @@ -6404,6 +6523,17 @@ LibraryManager.library = { _pthread_key_create.keys[key] = value; }, + pthread_cleanup_push: function(routine, arg) { + __ATEXIT__.push({ func: function() { FUNCTION_TABLE[routine](arg) } }) + _pthread_cleanup_push.level = __ATEXIT__.length; + }, + + pthread_cleanup_pop: function() { + assert(_pthread_cleanup_push.level == __ATEXIT__.length, 'cannot pop if something else added meanwhile!'); + __ATEXIT__.pop(); + _pthread_cleanup_push.level = __ATEXIT__.length; + }, + // ========================================================================== // malloc.h // ========================================================================== @@ -6833,6 +6963,18 @@ LibraryManager.library = { return eval(Pointer_stringify(ptr)); }, + emscripten_run_script_string: function(ptr) { + var s = eval(Pointer_stringify(ptr)); + var me = _emscripten_run_script_string; + if (!me.bufferSize || me.bufferSize < s.length+1) { + if (me.bufferSize) _free(me.buffer); + me.bufferSize = s.length+1; + me.buffer = _malloc(me.bufferSize); + } + writeStringToMemory(s, me.buffer); + return me.buffer; + }, + emscripten_random: function() { return Math.random(); }, diff --git a/src/library_gc.js b/src/library_gc.js index bf0a6aff4fc75..a06e2f0106269 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -16,6 +16,11 @@ if (GC_SUPPORT) { init: function() { assert(!GC.initted); GC.initted = true; + + _GC_finalize_on_demand = _malloc(4); setValue(_GC_finalize_on_demand, 0, 'i32') + _GC_java_finalization = _malloc(4); setValue(_GC_java_finalization, 0, 'i32'); + _GC_finalizer_notifier = _malloc(4); setValue(_GC_finalizer_notifier, 0, 'i32'); + if (ENVIRONMENT_IS_WEB) { setInterval(function() { GC.maybeCollect(); @@ -159,7 +164,13 @@ if (GC_SUPPORT) { GC_FORCE_COLLECT__deps: ['$GC'], GC_FORCE_COLLECT: function() { GC.collect(); - } + }, + + GC_finalize_on_demand: 0, + GC_java_finalization: 0, + GC_finalizer_notifier: 0, + + GC_enable_incremental: function(){}, }; mergeInto(LibraryManager.library, LibraryGC); diff --git a/src/library_gl.js b/src/library_gl.js index 991981960469e..0f28a6a0231c1 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -454,8 +454,7 @@ var LibraryGL = { }, glBufferSubData: function(target, offset, size, data) { - var floatArray = {{{ makeHEAPView('F32', 'data', 'data+size') }}}; - Module.ctx.bufferSubData(target, offset, floatArray); + Module.ctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); }, glIsBuffer: function(buffer) { @@ -2080,6 +2079,9 @@ var LibraryGL = { glVertex3fv: function(p) { _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}); }, + glVertex2fv: function(p) { + _glVertex2f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}); + }, glTexCoord2i: function(u, v) { #if ASSERTIONS @@ -2097,6 +2099,11 @@ var LibraryGL = { }, glColor4f: function(r, g, b, a) { + r = Math.max(Math.min(r, 1), 0); + g = Math.max(Math.min(g, 1), 0); + b = Math.max(Math.min(b, 1), 0); + a = Math.max(Math.min(a, 1), 0); + // TODO: make ub the default, not f, save a few mathops if (GL.immediate.mode) { var start = GL.immediate.vertexCounter << 2; diff --git a/src/library_sdl.js b/src/library_sdl.js index 4cb4ca6f1cb6f..1cd995349e2fe 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -898,7 +898,7 @@ var LibrarySDL = { }, SDL_GetError: function() { - return allocate(intArrayFromString("SDL is cool"), 'i8'); + return allocate(intArrayFromString("unknown SDL-emscripten error"), 'i8'); }, SDL_CreateRGBSurface: function(flags, width, height, depth, rmask, gmask, bmask, amask) { @@ -966,6 +966,16 @@ var LibrarySDL = { return _SDL_UpperBlit(src, srcrect, dst, dstrect); }, + zoomSurface: function(src, x, y, smooth) { + var srcData = SDL.surfaces[src]; + var w = srcData.width*x; + var h = srcData.height*y; + var ret = SDL.makeSurface(w, h, srcData.flags, false, 'zoomSurface'); + var dstData = SDL.surfaces[ret]; + dstData.ctx.drawImage(srcData.canvas, 0, 0, w, h); + return ret; + }, + SDL_SetAlpha: function(surf, flag, alpha) { SDL.surfaces[surf].alpha = alpha; }, @@ -1523,6 +1533,12 @@ var LibrarySDL = { SDL_GL_SwapBuffers: function() {}, + // TODO + + SDL_SetGamma: function(r, g, b) { + return -1; + }, + // Misc SDL_InitSubSystem: function(flags) { return 0 }, diff --git a/src/modules.js b/src/modules.js index 60b4ff873be2a..fd0ec35ef6bb6 100644 --- a/src/modules.js +++ b/src/modules.js @@ -5,11 +5,11 @@ var LLVM = { LINKAGES: set('private', 'linker_private', 'linker_private_weak', 'linker_private_weak_def_auto', 'internal', 'available_externally', 'linkonce', 'common', 'weak', 'appending', 'extern_weak', 'linkonce_odr', - 'weak_odr', 'externally_visible', 'dllimport', 'dllexport', 'unnamed_addr'), + 'weak_odr', 'externally_visible', 'dllimport', 'dllexport', 'unnamed_addr', 'thread_local'), VISIBILITIES: set('default', 'hidden', 'protected'), PARAM_ATTR: set('noalias', 'signext', 'zeroext', 'inreg', 'sret', 'nocapture', 'nest'), FUNC_ATTR: set('hidden', 'nounwind', 'define', 'inlinehint', '{'), - CALLING_CONVENTIONS: set('ccc', 'fastcc', 'coldcc', 'cc10', 'x86_fastcallcc', 'x86_stdcallcc'), + CALLING_CONVENTIONS: set('ccc', 'fastcc', 'coldcc', 'cc10', 'x86_fastcallcc', 'x86_stdcallcc', 'cc11'), ACCESS_OPTIONS: set('volatile', 'atomic'), INVOKE_MODIFIERS: set('alignstack', 'alwaysinline', 'inlinehint', 'naked', 'noimplicitfloat', 'noinline', 'alwaysinline attribute.', 'noredzone', 'noreturn', 'nounwind', 'optsize', 'readnone', 'readonly', 'ssp', 'sspreq'), SHIFTS: set('ashr', 'lshr', 'shl'), @@ -174,7 +174,10 @@ var PreProcessor = { }; var Variables = { - globals: {} + globals: {}, + indexedGlobals: {}, // for indexed globals, ident ==> index + // Used in calculation of indexed globals + nextIndexedOffset: 0 }; var Types = { @@ -207,8 +210,9 @@ var Types = { needAnalysis: {}, // Types noticed during parsing, that need analysis - preciseI64MathUsed: false // 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) + // 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) }; var Functions = { @@ -242,16 +246,14 @@ var Functions = { for (var ident in this.indexedFunctions) { vals[this.indexedFunctions[ident]] = ident; } - // Resolve multi-level aliases all the way down for (var i = 0; i < vals.length; i++) { while (1) { var varData = Variables.globals[vals[i]]; if (!(varData && varData.resolvedAlias)) break; - vals[i] = vals[varData.resolvedAlias]; + vals[i] = vals[+varData.resolvedAlias || eval(varData.resolvedAlias)]; // might need to eval to turn (6) into 6 } } - var indices = vals.toString().replace('"', ''); if (BUILD_AS_SHARED_LIB) { // Shared libraries reuse the parent's function table. @@ -264,6 +266,7 @@ var Functions = { var LibraryManager = { library: null, + loaded: false, load: function() { assert(!this.library); @@ -272,6 +275,8 @@ var LibraryManager = { for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } + + this.loaded = true; }, // Given an ident, see if it is an alias for something, and so forth, returning @@ -302,11 +307,21 @@ function cDefine(key) { var PassManager = { serialize: function() { - print('\n//FORWARDED_DATA:' + JSON.stringify({ - Types: Types, - Variables: Variables, - Functions: Functions - })); + if (phase == 'pre') { + print('\n//FORWARDED_DATA:' + JSON.stringify({ + Types: Types, + Variables: Variables, + Functions: Functions + })); + } else if (phase == 'funcs') { + print('\n//FORWARDED_DATA:' + JSON.stringify({ + Types: { preciseI64MathUsed: Types.preciseI64MathUsed }, + Functions: { + blockAddresses: Functions.blockAddresses, + indexedFunctions: Functions.indexedFunctions + } + })); + } }, load: function(json) { var data = JSON.parse(json); diff --git a/src/parseTools.js b/src/parseTools.js index e76d23be850cc..b2093da3930be 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -167,7 +167,15 @@ function isFunctionDef(token, out) { return !fail; } + +function isPossiblyFunctionType(type) { + // A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite. + var len = type.length; + return type[len-2] == ')' && type[len-1] == '*'; +} + function isFunctionType(type, out) { + if (!isPossiblyFunctionType(type)) return false; type = type.replace(/"[^"]+"/g, '".."'); var parts; // hackish, but quick splitting of function def parts. this must be fast as it happens a lot @@ -184,14 +192,12 @@ function isFunctionType(type, out) { return isType(parts[0]) && isFunctionDef({ text: text, item: tokenize(text.substr(1, text.length-2), true) }, out); } -function isType(type) { // TODO! - return isVoidType(type) || Runtime.isNumberType(type) || isStructType(type) || isPointerType(type) || isFunctionType(type); -} - -function isPossiblyFunctionType(type) { - // A quick but unreliable way to see if something is a function type. Yes is just 'maybe', no is definite. - var suffix = ')*'; - return type.substr(-suffix.length) == suffix; +var isTypeCache = {}; // quite hot, optimize as much as possible +function isType(type) { + if (type in isTypeCache) return isTypeCache[type]; + var ret = isPointerType(type) || isVoidType(type) || Runtime.isNumberType(type) || isStructType(type) || isFunctionType(type); + isTypeCache[type] = ret; + return ret; } function isVarArgsFunctionType(type) { @@ -350,6 +356,40 @@ function hasVarArgs(params) { return false; } +var UNINDEXABLE_GLOBALS = set( + '_llvm_global_ctors' // special-cased +); + +function noticePtr(ptr) { + if (!NAMED_GLOBALS && !LibraryManager.loaded) UNINDEXABLE_GLOBALS[ptr] = 1; // we cannot index globals referred to in the library, since they are used there by name +} + +function isIndexableGlobal(ident) { + if (!(ident in Variables.globals)) return false; + if (ident in UNINDEXABLE_GLOBALS) return false; + var data = Variables.globals[ident]; + return !data.alias && !data.external; +} + +function makeGlobalDef(ident) { + if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return ''; + return 'var ' + ident + ';'; // TODO: add option for namespacing or offsetting to allow reducing the number of globals +} + +function makeGlobalUse(ident) { + if (!NAMED_GLOBALS && isIndexableGlobal(ident)) return '(' + getFastValue('GLOBAL_BASE', '+', Variables.indexedGlobals[ident]) + ')'; + return ident; // TODO: add option for namespacing or offsetting to allow reducing the number of globals +} + +function sortGlobals(globals) { + var ks = keys(globals); + ks.sort(); + var inv = invertArray(ks); + return values(globals).sort(function(a, b) { + return inv[b.ident] - inv[a.ident]; + }); +} + function finalizeParam(param) { if (param.intertype in PARSABLE_LLVM_FUNCTIONS) { return finalizeLLVMFunctionCall(param); @@ -362,10 +402,9 @@ function finalizeParam(param) { return parseI64Constant(param.ident); } var ret = toNiceIdent(param.ident); - if (ret in Variables.globals && Variables.globals[ret].isString) { - ret = "STRING_TABLE." + ret; + if (ret in Variables.globals) { + ret = makeGlobalUse(ret); } - return ret; } } @@ -847,7 +886,6 @@ function indentify(text, indent) { // Correction tools function correctSpecificSign() { - assert(!(CORRECT_SIGNS >= 2 && !Debugging.on), 'Need debugging for line-specific corrections'); if (!Framework.currItem) return false; if (Framework.currItem.funcData.ident.indexOf('emscripten_autodebug') >= 0) return 1; // always correct in the autodebugger code! return (CORRECT_SIGNS === 2 && Debugging.getIdentifier() in CORRECT_SIGNS_LINES) || @@ -858,7 +896,6 @@ function correctSigns() { } function correctSpecificOverflow() { - assert(!(CORRECT_OVERFLOWS >= 2 && !Debugging.on), 'Need debugging for line-specific corrections'); if (!Framework.currItem) return false; return (CORRECT_OVERFLOWS === 2 && Debugging.getIdentifier() in CORRECT_OVERFLOWS_LINES) || (CORRECT_OVERFLOWS === 3 && !(Debugging.getIdentifier() in CORRECT_OVERFLOWS_LINES)); @@ -868,7 +905,6 @@ function correctOverflows() { } function correctSpecificRounding() { - assert(!(CORRECT_ROUNDINGS >= 2 && !Debugging.on), 'Need debugging for line-specific corrections'); if (!Framework.currItem) return false; return (CORRECT_ROUNDINGS === 2 && Debugging.getIdentifier() in CORRECT_ROUNDINGS_LINES) || (CORRECT_ROUNDINGS === 3 && !(Debugging.getIdentifier() in CORRECT_ROUNDINGS_LINES)); @@ -878,7 +914,6 @@ function correctRoundings() { } function checkSpecificSafeHeap() { - assert(!(SAFE_HEAP >= 2 && !Debugging.on), 'Need debugging for line-specific checks'); if (!Framework.currItem) return false; return (SAFE_HEAP === 2 && Debugging.getIdentifier() in SAFE_HEAP_LINES) || (SAFE_HEAP === 3 && !(Debugging.getIdentifier() in SAFE_HEAP_LINES)); @@ -905,6 +940,7 @@ function getHeapOffset(offset, type) { // See makeSetValue function makeGetValue(ptr, pos, type, noNeedFirst, unsigned, ignore, align, noSafe) { + noticePtr(ptr); if (UNALIGNED_MEMORY) align = 1; if (isStructType(type)) { var typeData = Types.types[type]; @@ -985,6 +1021,7 @@ function indexizeFunctions(value, type) { //! which means we should write to all slabs, ignore type differences if any on reads, etc. //! @param noNeedFirst Whether to ignore the offset in the pointer itself. function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore, align, noSafe, sep, forcedAlign) { + noticePtr(ptr); if (UNALIGNED_MEMORY && !forcedAlign) align = 1; sep = sep || ';'; if (isStructType(type)) { @@ -1056,6 +1093,7 @@ var SEEK_OPTIMAL_ALIGN_MIN = 20; var UNROLL_LOOP_MAX = 8; function makeSetValues(ptr, pos, value, type, num, align) { + noticePtr(ptr); function unroll(type, num, jump, value$) { jump = jump || 1; value$ = value$ || value; @@ -1105,6 +1143,8 @@ function makeSetValues(ptr, pos, value, type, num, align) { var TYPED_ARRAY_SET_MIN = Infinity; // .set() as memcpy seems to just slow us down function makeCopyValues(dest, src, num, type, modifier, align, sep) { + noticePtr(dest); + noticePtr(src); sep = sep || ';'; function unroll(type, num, jump) { jump = jump || 1; @@ -1258,7 +1298,7 @@ function makeGetPos(ptr) { var IHEAP_FHEAP = set('IHEAP', 'IHEAPU', 'FHEAP'); -function makePointer(slab, pos, allocator, type) { +function makePointer(slab, pos, allocator, type, ptr) { assert(type, 'makePointer requires type info'); if (slab.substr(0, 4) === 'HEAP' || (USE_TYPED_ARRAYS == 1 && slab in IHEAP_FHEAP)) return pos; var types = generateStructTypes(type); @@ -1288,7 +1328,27 @@ function makePointer(slab, pos, allocator, type) { types = de[0]; } } - return 'allocate(' + slab + ', ' + JSON.stringify(types) + (allocator ? ', ' + allocator : '') + ')'; + // JS engines sometimes say array initializers are too large. Work around that by chunking and calling concat to combine at runtime + var chunkSize = 10240; + function chunkify(array) { + // break very large slabs into parts + var ret = ''; + var index = 0; + while (index < array.length) { + ret = (ret ? ret + '.concat(' : '') + '[' + array.slice(index, index + chunkSize).map(JSON.stringify) + ']' + (ret ? ')' : ''); + index += chunkSize; + } + return ret; + } + if (typeof slab == 'string' && evaled && evaled.length > chunkSize) { + slab = chunkify(evaled); + } + if (typeof types != 'string' && types.length > chunkSize) { + types = chunkify(types); + } else { + types = JSON.stringify(types); + } + return 'allocate(' + slab + ', ' + types + (allocator ? ', ' + allocator : '') + (allocator == 'ALLOC_NONE' ? ', ' + ptr : '') + ')'; } function makeGetSlabs(ptr, type, allowMultiple, unsigned) { @@ -1470,8 +1530,8 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { } } else if (param.intertype == 'value') { ret = param.ident; - if (ret in Variables.globals && Variables.globals[ret].isString) { - ret = "STRING_TABLE." + ret; + if (ret in Variables.globals) { + ret = makeGlobalUse(ret); } if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) { ret = parseI64Constant(ret); @@ -1710,7 +1770,7 @@ function processMathop(item) { } } } - case 'uitofp': case 'sitofp': return low1 + ' + ' + high1 + '*4294967296'; + case 'uitofp': case 'sitofp': return RuntimeGenerator.makeBigInt(low1, high1, op[0] == 'u'); case 'fptoui': case 'fptosi': return finish(splitI64(idents[0])); case 'icmp': { switch (variant) { @@ -2000,9 +2060,7 @@ function parseBlockAddress(segment) { } function finalizeBlockAddress(param) { - assert(param.func in Functions.blockAddresses); - assert(param.label in Functions.blockAddresses[param.func]); - return Functions.blockAddresses[param.func][param.label]; + return '{{{ BA_' + param.func + '|' + param.label + ' }}}'; // something python will replace later } function stripCorrections(param) { diff --git a/src/postamble.js b/src/postamble.js index a4c4301825564..144b5af9c9130 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -82,7 +82,7 @@ function run(args) { return doRun(); } } -Module['run'] = run; +Module['run'] = Module.run = run; // {{PRE_RUN_ADDITIONS}} diff --git a/src/preamble.js b/src/preamble.js index 16865bd0e4d4d..0917f9773e295 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -276,6 +276,8 @@ Module['printProfiling'] = printProfiling; //======================================== var __THREW__ = false; // Used in checking for thrown exceptions. +var setjmpId = 1; // Used in setjmp/longjmp +var setjmpLabels = {}; var ABORT = false; @@ -472,9 +474,11 @@ Module['getValue'] = getValue; var ALLOC_NORMAL = 0; // Tries to use _malloc() var ALLOC_STACK = 1; // Lives for the duration of the current function call var ALLOC_STATIC = 2; // Cannot be freed +var ALLOC_NONE = 3; // Do not allocate Module['ALLOC_NORMAL'] = ALLOC_NORMAL; Module['ALLOC_STACK'] = ALLOC_STACK; Module['ALLOC_STATIC'] = ALLOC_STATIC; +Module['ALLOC_NONE'] = ALLOC_NONE; // allocate(): This is for internal use. You can use it yourself as well, but the interface // is a little tricky (see docs right below). The reason is that it is optimized @@ -489,7 +493,7 @@ Module['ALLOC_STATIC'] = ALLOC_STATIC; // is initial data - if @slab is a number, then this does not matter at all and is // ignored. // @allocator: How to allocate memory, see ALLOC_* -function allocate(slab, types, allocator) { +function allocate(slab, types, allocator, ptr) { var zeroinit, size; if (typeof slab === 'number') { zeroinit = true; @@ -501,7 +505,12 @@ function allocate(slab, types, allocator) { var singleType = typeof types === 'string' ? types : null; - var ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + var ret; + if (allocator == ALLOC_NONE) { + ret = ptr; + } else { + ret = [_malloc, Runtime.stackAlloc, Runtime.staticAlloc][allocator === undefined ? ALLOC_STATIC : allocator](Math.max(size, singleType ? 1 : types.length)); + } if (zeroinit) { _memset(ret, 0, size); @@ -544,6 +553,9 @@ function Pointer_stringify(ptr, /* optional */ length) { var i = 0; var t; while (1) { +#if ASSERTIONS + assert(i < TOTAL_MEMORY); +#endif t = {{{ makeGetValue('ptr', 'i', 'i8', 0, 1) }}}; if (nullTerminated && t == 0) break; ret += utf8.processCChar(t); @@ -602,26 +614,26 @@ function enlargeMemory() { } #if USE_TYPED_ARRAYS == 1 var oldIHEAP = IHEAP; - HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); + Module['HEAP'] = Module['IHEAP'] = HEAP = IHEAP = new Int32Array(TOTAL_MEMORY); IHEAP.set(oldIHEAP); IHEAPU = new Uint32Array(IHEAP.buffer); #if USE_FHEAP var oldFHEAP = FHEAP; - FHEAP = new Float64Array(TOTAL_MEMORY); + Module['FHEAP'] = FHEAP = new Float64Array(TOTAL_MEMORY); FHEAP.set(oldFHEAP); #endif #endif #if USE_TYPED_ARRAYS == 2 var oldHEAP8 = HEAP8; var buffer = new ArrayBuffer(TOTAL_MEMORY); - HEAP8 = new Int8Array(buffer); - HEAP16 = new Int16Array(buffer); - HEAP32 = new Int32Array(buffer); - HEAPU8 = new Uint8Array(buffer); - HEAPU16 = new Uint16Array(buffer); - HEAPU32 = new Uint32Array(buffer); - HEAPF32 = new Float32Array(buffer); - HEAPF64 = new Float64Array(buffer); + Module['HEAP8'] = HEAP8 = new Int8Array(buffer); + Module['HEAP16'] = HEAP16 = new Int16Array(buffer); + Module['HEAP32'] = HEAP32 = new Int32Array(buffer); + Module['HEAPU8'] = HEAPU8 = new Uint8Array(buffer); + Module['HEAPU16'] = HEAPU16 = new Uint16Array(buffer); + Module['HEAPU32'] = HEAPU32 = new Uint32Array(buffer); + Module['HEAPF32'] = HEAPF32 = new Float32Array(buffer); + Module['HEAPF64'] = HEAPF64 = new Float64Array(buffer); HEAP8.set(oldHEAP8); #endif #endif @@ -750,7 +762,11 @@ function exitRuntime() { function String_len(ptr) { var i = ptr; - while ({{{ makeGetValue('i++', '0', 'i8') }}}) {}; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds + while ({{{ makeGetValue('i++', '0', 'i8') }}}) { // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds +#if ASSERTIONS + assert(i < TOTAL_MEMORY); +#endif + } return i - ptr - 1; } Module['String_len'] = String_len; @@ -806,8 +822,6 @@ function writeArrayToMemory(array, buffer) { } Module['writeArrayToMemory'] = writeArrayToMemory; -var STRING_TABLE = []; - {{{ unSign }}} {{{ reSign }}} diff --git a/src/preamble_sharedlib.js b/src/preamble_sharedlib.js index af204e2f5b993..2a071f6bcf2d4 100644 --- a/src/preamble_sharedlib.js +++ b/src/preamble_sharedlib.js @@ -16,7 +16,6 @@ function callRuntimeCallbacks(callbacks) { } var __ATINIT__ = []; // functions called during startup -var STRING_TABLE = []; function initRuntime() { callRuntimeCallbacks(__ATINIT__); diff --git a/src/settings.js b/src/settings.js index 5d4299a4b5879..c4d1df486e070 100644 --- a/src/settings.js +++ b/src/settings.js @@ -88,6 +88,10 @@ var PRECISE_I64_MATH = 1; // If enabled, i64 addition etc. is emulated - which i // Note that we do not catch 32-bit multiplication by default (which must be done in // 64 bits for high values for full precision) - you must manually set PRECISE_I32_MUL // for that. + // If set to 2, we always include the i64 math code, which is necessary in the case + // that we can't know at compile time that 64-bit math is needed. For example, if you + // print 64-bit values with printf, but never add them, we can't know at compile time + // and you need to set this to 2. var PRECISE_I32_MUL = 0; // If enabled, i64 math is done in i32 multiplication. This is necessary if the values // exceed the JS double-integer limit of ~52 bits. This option can normally be disabled // because generally i32 multiplication works ok without it, and enabling it has a big @@ -128,6 +132,12 @@ var SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations var LABEL_DEBUG = 0; // 1: Print out functions as we enter them // 2: Also print out each label as we enter it +var LABEL_FUNCTION_FILTERS = []; // Filters for function label debug. + // The items for this array will be used + // as filters for function names. Only the + // labels of functions that is equaled to + // one of the filters are printed out + // When the array is empty, the filter is disabled. var EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code var LIBRARY_DEBUG = 0; // Print out when we enter a library call (library*.js). You can also unset @@ -190,11 +200,17 @@ var PGO = 0; // Profile-guided optimization. // All CORRECT_* options default to 1 with PGO builds. // See https://github.com/kripken/emscripten/wiki/Optimizing-Code for more info +var NAMED_GLOBALS = 1; // If 1, we use global variables for globals. Otherwise + // they are referred to by a base plus an offset (called an indexed global), + // saving global variables but adding runtime overhead. + var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. -var EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']; // Functions that are explicitly exported, so they are guaranteed to - // be accessible outside of the generated code even after running closure compiler. - // Note the necessary prefix of "_". +var EXPORT_ALL = 0; // If true, we export all the symbols +var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported. These functions are kept alive + // through LLVM dead code elimination, and also made accessible outside of + // the generated code even after running closure compiler (on "Module"). + // Note the necessary prefix of "_". var DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = ['memcpy', 'memset', 'malloc', 'free', '$Browser']; // JS library functions (C functions implemented in JS) // that we include by default. If you want to make sure @@ -277,6 +293,14 @@ var SMALL_XHR_CHUNKS = 0; // Use small chunk size for binary synchronous XHR's i // Used for testing. // See test_chunked_synchronous_xhr in runner.py and library.js. +var HEADLESS = 0; // If 1, will include shim code that tries to 'fake' a browser + // environment, in order to let you run a browser program (say, + // using SDL) in the shell. Obviously nothing is rendered, but + // this can be useful for benchmarking and debugging if actual + // rendering is not the issue. Note that the shim code is + // very partial - it is hard to fake a whole browser! - so + // keep your expectations low for this to work. + var NECESSARY_BLOCKADDRS = []; // List of (function, block) for all block addresses that are taken. // Compiler debugging options diff --git a/src/utility.js b/src/utility.js index 632ee08c674bc..84b50ce938cca 100644 --- a/src/utility.js +++ b/src/utility.js @@ -184,7 +184,7 @@ function dprint() { text = text(); // Allows deferred calculation, so dprints don't slow us down when not needed } text = DPRINT_INDENT + '// ' + text; - print(text); + printErr(text); } var PROF_ORIGIN = Date.now(); @@ -282,6 +282,14 @@ function setIntersect(x, y) { return ret; } +function invertArray(x) { + var ret = {}; + for (var i = 0; i < x.length; i++) { + ret[x[i]] = i; + } + return ret; +} + function copy(x) { return JSON.parse(JSON.stringify(x)); } diff --git a/system/include/SDL/SDL_rotozoom.h b/system/include/SDL/SDL_rotozoom.h new file mode 100644 index 0000000000000..65b914a970015 --- /dev/null +++ b/system/include/SDL/SDL_rotozoom.h @@ -0,0 +1,123 @@ +/* + +SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces + +Copyright (C) 2001-2011 Andreas Schiffler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. + +Andreas Schiffler -- aschiffler at ferzkopp dot net + +*/ + +#ifndef _SDL_rotozoom_h +#define _SDL_rotozoom_h + +#include + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +#include "SDL.h" + + /* ---- Defines */ + + /*! + \brief Disable anti-aliasing (no smoothing). + */ +#define SMOOTHING_OFF 0 + + /*! + \brief Enable anti-aliasing (smoothing). + */ +#define SMOOTHING_ON 1 + + /* ---- Function Prototypes */ + +#ifdef _MSC_VER +# if defined(DLL_EXPORT) && !defined(LIBSDL_GFX_DLL_IMPORT) +# define SDL_ROTOZOOM_SCOPE __declspec(dllexport) +# else +# ifdef LIBSDL_GFX_DLL_IMPORT +# define SDL_ROTOZOOM_SCOPE __declspec(dllimport) +# endif +# endif +#endif +#ifndef SDL_ROTOZOOM_SCOPE +# define SDL_ROTOZOOM_SCOPE extern +#endif + + /* + + Rotozoom functions + + */ + + SDL_ROTOZOOM_SCOPE SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth); + + SDL_ROTOZOOM_SCOPE SDL_Surface *rotozoomSurfaceXY + (SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth); + + + SDL_ROTOZOOM_SCOPE void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, + int *dstheight); + + SDL_ROTOZOOM_SCOPE void rotozoomSurfaceSizeXY + (int width, int height, double angle, double zoomx, double zoomy, + int *dstwidth, int *dstheight); + + /* + + Zooming functions + + */ + + SDL_ROTOZOOM_SCOPE SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth); + + SDL_ROTOZOOM_SCOPE void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight); + + /* + + Shrinking functions + + */ + + SDL_ROTOZOOM_SCOPE SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory); + + /* + + Specialized rotation functions + + */ + + SDL_ROTOZOOM_SCOPE SDL_Surface* rotateSurface90Degrees(SDL_Surface* src, int numClockwiseTurns); + + /* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* _SDL_rotozoom_h */ diff --git a/system/include/dlfcn.h b/system/include/dlfcn.h index 6854dab06bf85..3a4edd004bf82 100644 --- a/system/include/dlfcn.h +++ b/system/include/dlfcn.h @@ -3,10 +3,11 @@ extern "C" { #endif -#define RTLD_LAZY 1 -#define RTLD_NOW 2 -#define RTLD_GLOBAL 4 -#define RTLD_LOCAL 8 +#define RTLD_DEFAULT 0 +#define RTLD_LAZY 1 +#define RTLD_NOW 2 +#define RTLD_GLOBAL 4 +#define RTLD_LOCAL 8 typedef struct { const char *dli_fname; diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h index 4a82eacb50352..55fda986c752f 100644 --- a/system/include/emscripten/bind.h +++ b/system/include/emscripten/bind.h @@ -218,7 +218,7 @@ namespace emscripten { template struct MethodInvoker { typedef ReturnType (ClassType::*MemberPointer)(Args...); - typename internal::BindingType::WireType invoke( + static typename internal::BindingType::WireType invoke( ClassType* ptr, const MemberPointer& method, typename internal::BindingType::WireType... args @@ -445,6 +445,7 @@ namespace emscripten { args.count, args.types, reinterpret_cast(&internal::raw_constructor)); + return *this; } template @@ -557,6 +558,7 @@ namespace emscripten { } new(get()) T(v); initialized = true; + return *this; } private: diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index 871a716298a41..3eefe0b876809 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -31,6 +31,7 @@ extern "C" { */ extern void emscripten_run_script(const char *script); extern int emscripten_run_script_int(const char *script); +extern char *emscripten_run_script_string(const char *script); // uses a single buffer - shared between calls! extern void emscripten_async_run_script(const char *script, int millis); /* diff --git a/system/include/gc.h b/system/include/gc.h index 996bc9eca08fd..e0419dcb742ea 100644 --- a/system/include/gc.h +++ b/system/include/gc.h @@ -8,7 +8,7 @@ extern "C" { #endif -void __attribute__((used)) __GC_KEEPALIVE__() { +static void __attribute__((used)) __GC_KEEPALIVE__() { // Force inclusion of necessary dlmalloc functions static int times = 1; void *x = malloc(times); @@ -44,6 +44,14 @@ void GC_MAYBE_COLLECT(); /* Forces a GC. Mainly useful for testing, but call it if you know a good time to GC in your app. */ void GC_FORCE_COLLECT(); +typedef void (*GC_finalization_proc)(void *func, void *arg); +extern void (*GC_finalizer_notifier)(); + +extern int GC_finalize_on_demand; +extern int GC_java_finalization; + +void GC_enable_incremental(); + #ifdef __cplusplus } #endif diff --git a/system/include/libc/limits.h b/system/include/libc/limits.h index 93362b88db905..3f1def0f84cda 100644 --- a/system/include/libc/limits.h +++ b/system/include/libc/limits.h @@ -144,3 +144,12 @@ #define PATH_MAX 4096 #endif +/* XXX EMSCRIPTEN */ +#ifndef _LIBC_LIMITS2_H_ +#define _LIBC_LIMITS2_H_ 1 + +#define _POSIX_PATH_MAX PATH_MAX +#define PTHREAD_STACK_MIN 0 + +#endif + diff --git a/system/include/libc/sys/_default_fcntl.h b/system/include/libc/sys/_default_fcntl.h index 188e25c45609e..379147ed56dd1 100644 --- a/system/include/libc/sys/_default_fcntl.h +++ b/system/include/libc/sys/_default_fcntl.h @@ -207,6 +207,8 @@ extern int _open64 _PARAMS ((const char *, int, ...)); /* XXX Emscripten */ #define POSIX_FADV_DONTNEED 135 +#define POSIX_FADV_SEQUENTIAL 136 +#define POSIX_FADV_RANDOM 137 int posix_fadvise(int fd, off_t offset, off_t len, int advice); int posix_fallocate(int fd, off_t offset, off_t len); #define LOCK_SH 1 diff --git a/system/include/libc/sys/features.h b/system/include/libc/sys/features.h index 9faf2b899bb71..87a520a0ab706 100644 --- a/system/include/libc/sys/features.h +++ b/system/include/libc/sys/features.h @@ -25,6 +25,10 @@ extern "C" { #endif +#if EMSCRIPTEN +#define _POSIX_REALTIME_SIGNALS 1 +#endif + /* RTEMS adheres to POSIX -- 1003.1b with some features from annexes. */ #ifdef __rtems__ diff --git a/system/include/libc/sys/resource.h b/system/include/libc/sys/resource.h index f39dcb33e888e..abcfd34d46c8b 100644 --- a/system/include/libc/sys/resource.h +++ b/system/include/libc/sys/resource.h @@ -27,6 +27,7 @@ struct rusage { }; /* XXX Emscripten */ +#define PRIO_PROCESS 0 int getrusage(int who, struct rusage *r_usage); /* XXX Emscripten */ diff --git a/system/include/libc/sys/signal.h b/system/include/libc/sys/signal.h index 543b626e7a093..910ccf7858838 100644 --- a/system/include/libc/sys/signal.h +++ b/system/include/libc/sys/signal.h @@ -114,6 +114,7 @@ struct sigaction _sig_func_ptr sa_handler; sigset_t sa_mask; int sa_flags; + void (*sa_sigaction)(int, siginfo_t *, void *); /* XXX EMSCRIPTEN */ }; #endif /* defined(__rtems__) */ diff --git a/system/include/net/netinet/in.h b/system/include/net/netinet/in.h index 2e4e4e573343b..2ac98dfed848e 100644 --- a/system/include/net/netinet/in.h +++ b/system/include/net/netinet/in.h @@ -48,6 +48,26 @@ struct ip_mreq { struct in_addr imr_interface; }; +#define IP_MULTICAST_IF 32 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 +#define IP_UNBLOCK_SOURCE 37 +#define IP_BLOCK_SOURCE 38 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#define IP_MSFILTER 41 +#define MCAST_JOIN_GROUP 42 +#define MCAST_BLOCK_SOURCE 43 +#define MCAST_UNBLOCK_SOURCE 44 +#define MCAST_LEAVE_GROUP 45 +#define MCAST_JOIN_SOURCE_GROUP 46 +#define MCAST_LEAVE_SOURCE_GROUP 47 +#define MCAST_MSFILTER 48 +#define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 + #ifdef __cplusplus } #endif diff --git a/system/include/netdb.h b/system/include/netdb.h index 20f876dffe5b8..6e06944881bf9 100644 --- a/system/include/netdb.h +++ b/system/include/netdb.h @@ -9,6 +9,7 @@ extern "C" { #define TRY_AGAIN 2 #define NO_RECOVERY 3 #define NO_DATA 4 +#define NO_ADDRESS 5 #define IP_TOS 1 #define IP_TTL 2 @@ -48,6 +49,8 @@ void endhostent(void); void herror(const char* s); const char* hstrerror(int err); +extern int h_errno; + #ifdef __cplusplus } #endif diff --git a/system/include/sys/ioctl.h b/system/include/sys/ioctl.h index 12857ae3bc669..c54d4ccc57cea 100644 --- a/system/include/sys/ioctl.h +++ b/system/include/sys/ioctl.h @@ -12,6 +12,7 @@ extern "C" { #define TIOCGWINSZ 80 // bogus #define FIONREAD 1 +#define FIONBIO 2 int ioctl(int d, int request, ...); diff --git a/system/include/sys/poll.h b/system/include/sys/poll.h index 55e852376a409..7521ed0e7d8db 100644 --- a/system/include/sys/poll.h +++ b/system/include/sys/poll.h @@ -11,6 +11,7 @@ extern "C" { #define POLLNVAL 4 #define POLLERR 8 #define POLLHUP 16 +#define POLLPRI 32 struct pollfd { int fd; diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h index e33dbefdfd390..10ba5ce8a653d 100644 --- a/system/include/sys/socket.h +++ b/system/include/sys/socket.h @@ -11,8 +11,11 @@ extern "C" { // Note that the values of these constants are mostly arbitrary numbers. #define SOMAXCONN 128 +#define PF_LOCAL 1 +#define PF_UNIX PF_LOCAL #define PF_INET 2 #define SO_BROADCAST 6 +#define AF_UNIX PF_UNIX #define AF_UNSPEC 100 #define SOCK_STREAM 200 @@ -25,8 +28,14 @@ extern "C" { #define SO_LINGER 70 #define SO_NOSIGPIPE 80 #define SO_KEEPALIVE 90 +#define SO_OOBINLINE 100 +#define SO_NO_CHECK 110 +#define SO_PRIORITY 120 +#define SO_LINGER 130 +#define SO_BSDCOMPAT 140 -#define SHUT_RDWR 1 +#define SHUT_RD 1 +#define SHUT_RDWR 2 typedef unsigned int sa_family_t; #define AF_INET 1 @@ -72,6 +81,13 @@ struct linger { int l_linger; }; +#define SIOCATMARK 0x8905 + +#define SOCK_RAW 111 +#define SOCK_SEQPACKET 555 + +#define PF_APPLETALK 5 + #ifdef __cplusplus } #endif diff --git a/system/include/sys/statvfs.h b/system/include/sys/statvfs.h index 192be153e572e..8e17f817a961a 100644 --- a/system/include/sys/statvfs.h +++ b/system/include/sys/statvfs.h @@ -22,6 +22,8 @@ struct statvfs { int statvfs(const char *path, struct statvfs *s); +#define ST_RDONLY 0 + #ifdef __cplusplus } #endif diff --git a/system/include/sys/un.h b/system/include/sys/un.h new file mode 100644 index 0000000000000..b70425fbe7cf1 --- /dev/null +++ b/system/include/sys/un.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)un.h 8.3 (Berkeley) 2/19/95 + * $FreeBSD$ + */ + +#ifndef _SYS_UN_H_ +#define _SYS_UN_H_ + +#include +#include + +/* + * Definitions for UNIX IPC domain. + */ +struct sockaddr_un { + unsigned char sun_len; /* sockaddr len including null */ + sa_family_t sun_family; /* AF_UNIX */ + char sun_path[104]; /* path name (gag) */ +}; + +#if __BSD_VISIBLE + +/* Socket options. */ +#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */ +#define LOCAL_CREDS 0x002 /* pass credentials to receiver */ +#define LOCAL_CONNWAIT 0x004 /* connects block until accepted */ + +#ifndef _KERNEL + +/* actual length of an initialized sockaddr_un */ +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) + +#endif /* !_KERNEL */ + +#endif /* __BSD_VISIBLE */ + +#endif /* !_SYS_UN_H_ */ diff --git a/system/lib/libcxxabi/CREDITS.TXT b/system/lib/libcxxabi/CREDITS.TXT index a99245f733d70..231dfd334593e 100644 --- a/system/lib/libcxxabi/CREDITS.TXT +++ b/system/lib/libcxxabi/CREDITS.TXT @@ -13,10 +13,22 @@ E: hhinnant@apple.com D: Architect and primary coauthor of libc++abi N: Marshall Clow -E: marshall@idio.com E: mclow.lists@gmail.com -E: mclow@qualcomm.com +E: marshall@idio.com D: Architect and primary coauthor of libc++abi N: Nick Kledzik E: kledzik@apple.com + +N: Andrew Morrow +E: andrew.c.morrow@gmail.com +D: Minor patches and fixes + +N: Erik Olofsson +E: erik.olofsson@hansoft.se +E: erik@olofsson.info +D: Minor patches and fixes + +N: Nowar Gu +E: wenhan.gu@gmail.com +D: Minor patches and fixes diff --git a/system/lib/libcxxabi/include/cxxabi.h b/system/lib/libcxxabi/include/cxxabi.h index b08fba0e20e45..66ef6d46a2a59 100644 --- a/system/lib/libcxxabi/include/cxxabi.h +++ b/system/lib/libcxxabi/include/cxxabi.h @@ -64,7 +64,7 @@ extern LIBCXXABI_NORETURN void __cxa_pure_virtual(void); extern LIBCXXABI_NORETURN void __cxa_deleted_virtual(void); // 3.3.2 One-time Construction API -#ifdef LIBCXXABI_ARMEABI +#if __arm__ extern int __cxa_guard_acquire(uint32_t*); extern void __cxa_guard_release(uint32_t*); extern void __cxa_guard_abort(uint32_t*); diff --git a/system/lib/libcxxabi/lib/buildit b/system/lib/libcxxabi/lib/buildit index d1d2acdaf9b49..75a7028ab7b0b 100755 --- a/system/lib/libcxxabi/lib/buildit +++ b/system/lib/libcxxabi/lib/buildit @@ -27,7 +27,12 @@ then RC_ProjectSourceVersion=1 fi -EXTRA_FLAGS="-std=c++0x -stdlib=libc++ -fstrict-aliasing -Wstrict-aliasing=2 -Wnewline-eof" +EXTRA_FLAGS="-std=c++0x -stdlib=libc++ -fstrict-aliasing -Wstrict-aliasing=2 \ + -Wsign-conversion -Wshadow -Wconversion -Wunused-variable \ + -Wmissing-field-initializers -Wchar-subscripts -Wmismatched-tags \ + -Wmissing-braces -Wshorten-64-to-32 -Wsign-compare \ + -Wstrict-aliasing=2 -Wstrict-overflow=4 -Wunused-parameter \ + -Wnewline-eof" case $TRIPLE in *-apple-*) @@ -48,6 +53,10 @@ case $TRIPLE in -compatibility_version 1 \ -install_name /usr/lib/libc++abi.dylib \ -lSystem" + if [ -f "${SDKROOT}/usr/local/lib/libCrashReporterClient.a" ] + then + LDSHARED_FLAGS+=" -lCrashReporterClient" + fi ;; *-*-mingw*) # FIXME: removing libgcc and libsupc++ dependencies means porting libcxxrt and LLVM/compiler-rt diff --git a/system/lib/libcxxabi/readme.txt b/system/lib/libcxxabi/readme.txt index 0be9a318c76d2..fde423c0935f2 100644 --- a/system/lib/libcxxabi/readme.txt +++ b/system/lib/libcxxabi/readme.txt @@ -1 +1 @@ -These files are from libcxxabi, svn revision 151132, Feb 22 2012 +These files are from libcxxabi, svn revision 169402, Dec 10 2012 diff --git a/system/lib/libcxxabi/src/abort_message.cpp b/system/lib/libcxxabi/src/abort_message.cpp index 7beb86b5bbdd4..3da4ef67002f0 100644 --- a/system/lib/libcxxabi/src/abort_message.cpp +++ b/system/lib/libcxxabi/src/abort_message.cpp @@ -18,16 +18,6 @@ # if defined(__has_include) && __has_include() # define HAVE_CRASHREPORTERCLIENT_H 1 # include - - // If any clients of llvm try to link to libCrashReporterClient.a themselves, - // only one crash info struct will be used. - extern "C" { - CRASH_REPORTER_CLIENT_HIDDEN - struct crashreporter_annotations_t gCRAnnotations - __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) - = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 }; - } - # endif #endif diff --git a/system/lib/libcxxabi/src/cxa_demangle.cpp b/system/lib/libcxxabi/src/cxa_demangle.cpp index 1135c99b1206c..c1e126035fbc1 100644 --- a/system/lib/libcxxabi/src/cxa_demangle.cpp +++ b/system/lib/libcxxabi/src/cxa_demangle.cpp @@ -17,7 +17,6 @@ #include #include - #ifdef DEBUGGING #include @@ -42,8 +41,8 @@ class __node size_t __size_; __node* __left_; __node* __right_; - long double __value_; long __cached_size_; + long double __value_; public: __node() : __name_(0), __size_(0), __left_(0), __right_(0), __cached_size_(-1) @@ -64,8 +63,8 @@ class __node virtual size_t size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = first_size() + second_size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(first_size() + second_size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const {return buf;} virtual char* second_demangled_name(char* buf) const {return buf;} @@ -79,7 +78,7 @@ class __node return get_demangled_name(buf); } - virtual bool ends_with_template(bool parsing = false) const + virtual bool ends_with_template(bool /*parsing*/ = false) const { return false; } @@ -134,9 +133,15 @@ void display(__node* x, int indent = 0) { for (int i = 0; i < 2*indent; ++i) printf(" "); - std::string buf(x->size(), '\0'); - x->get_demangled_name(&buf.front()); - printf("%s %s, %p\n", typeid(*x).name(), buf.c_str(), x); + size_t sz = x->size(); + char* buf = (char*)calloc(sz+10, 1); + x->get_demangled_name(buf); + printf("%s [%ld] %s, %p\n", typeid(*x).name(), sz, buf, x); + if (strlen(buf) != sz) + { + printf("strlen(buf) = %ld and size = %ld\n", strlen(buf), sz); + } + free(buf); display(x->__left_, indent+1); display(x->__right_, indent+1); } @@ -157,8 +162,8 @@ class __vtable virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -188,8 +193,8 @@ class __VTT virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -220,9 +225,9 @@ class __construction_vtable virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __left_->size() - + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__left_->size() + + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -260,8 +265,8 @@ class __typeinfo virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -291,8 +296,8 @@ class __typeinfo_name virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -322,8 +327,8 @@ class __covariant_return_thunk virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = n + static_cast(__right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -353,8 +358,8 @@ class __virtual_thunk virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -384,8 +389,8 @@ class __non_virtual_thunk virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -415,8 +420,8 @@ class __guard_variable virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -442,8 +447,8 @@ class __reference_temporary virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -460,7 +465,7 @@ class __source_name : public __node { public: - __source_name(const char* __name, unsigned __size) + __source_name(const char* __name, size_t __size) { __name_ = __name; __size_ = __size; @@ -473,9 +478,9 @@ class __source_name if (__size_ >= 10 && strncmp(__name_, "_GLOBAL__N", 10) == 0) const_cast(__cached_size_) = 21; else - const_cast(__cached_size_) = __size_; + const_cast(__cached_size_) = static_cast(__size_); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -553,11 +558,13 @@ class __operator_logical_and if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator&&") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -603,11 +610,11 @@ class __operator_addressof if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator&") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -649,11 +656,13 @@ class __operator_bit_and if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator&") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -700,11 +709,13 @@ class __operator_and_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator&=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -751,11 +762,13 @@ class __operator_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -801,11 +814,11 @@ class __operator_alignof_type if (__cached_size_ == -1) { if (__right_) - const_cast(__cached_size_) = __right_->size() + 10; + const_cast(__cached_size_) = static_cast(__right_->size() + 10); else const_cast(__cached_size_) = sizeof("operator alignof") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -846,11 +859,11 @@ class __operator_alignof_expression if (__cached_size_ == -1) { if (__right_) - const_cast(__cached_size_) = __right_->size() + 10; + const_cast(__cached_size_) = static_cast(__right_->size() + 10); else const_cast(__cached_size_) = sizeof("operator alignof") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -905,11 +918,13 @@ class __operator_comma if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator,") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -955,11 +970,11 @@ class __operator_tilda if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator~") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1015,9 +1030,9 @@ class __operator_cast } else off = n + __right_->size();; - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1061,13 +1076,14 @@ class __cast_literal { __left_ = type; __name_ = f; - __size_ = l - f; + __size_ = static_cast(l - f); } virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = 2 + __left_->size() + __size_; - return __cached_size_; + const_cast(__cached_size_) = static_cast(2 + + __left_->size() + __size_); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1098,11 +1114,11 @@ class __operator_dereference if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator*") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1144,11 +1160,13 @@ class __operator_divide if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator/") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1195,11 +1213,13 @@ class __operator_divide_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator/=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1246,11 +1266,13 @@ class __operator_xor if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator^") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1297,11 +1319,13 @@ class __operator_xor_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator^=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1348,11 +1372,13 @@ class __operator_equality if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator==") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1399,11 +1425,13 @@ class __operator_greater_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator>=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1450,11 +1478,13 @@ class __operator_greater if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 9 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 9 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator>") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1516,11 +1546,13 @@ class __operator_less_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator<=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1567,11 +1599,13 @@ class __operator_less if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator<") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1618,11 +1652,13 @@ class __operator_left_shift if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator<<") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1669,11 +1705,13 @@ class __operator_left_shift_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 9 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 9 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator<<=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1720,11 +1758,13 @@ class __operator_minus if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator-") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1771,11 +1811,13 @@ class __operator_minus_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator-=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1822,11 +1864,13 @@ class __operator_times if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator*") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1873,11 +1917,13 @@ class __operator_times_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator*=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1924,11 +1970,11 @@ class __operator_decrement if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 4+__left_->size(); + const_cast(__cached_size_) = static_cast(4 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator--") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -1983,11 +2029,13 @@ class __operator_not_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator!=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2033,11 +2081,11 @@ class __operator_negate if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator-") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2078,11 +2126,11 @@ class __operator_logical_not if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator!") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2124,11 +2172,13 @@ class __operator_logical_or if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator||") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2175,11 +2225,13 @@ class __operator_bit_or if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator|") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2226,11 +2278,13 @@ class __operator_or_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator|=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2277,11 +2331,13 @@ class __operator_pointer_to_member if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 9 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 9 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator->*") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2328,11 +2384,13 @@ class __operator_plus if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator+") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2379,11 +2437,13 @@ class __operator_plus_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator+=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2430,11 +2490,11 @@ class __operator_increment if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 4+__left_->size(); + const_cast(__cached_size_) = static_cast(4 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator++") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2488,11 +2548,11 @@ class __operator_unary_plus if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = 3+__left_->size(); + const_cast(__cached_size_) = static_cast(3 + __left_->size()); else const_cast(__cached_size_) = sizeof("operator+") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2534,11 +2594,13 @@ class __operator_arrow if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator->") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2588,12 +2650,15 @@ class __operator_conditional if (__left_) { __node* op1 = (__node*)__name_; - const_cast(__cached_size_) = op1->size() + __left_->size() + 12 + __right_->size(); + const_cast(__cached_size_) = static_cast( + op1->size() + + __left_->size() + 12 + + __right_->size()); } else const_cast(__cached_size_) = sizeof("operator?") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2646,11 +2711,13 @@ class __operator_mod if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 7 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 7 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator%") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2697,11 +2764,13 @@ class __operator_mod_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator%=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2748,11 +2817,13 @@ class __operator_right_shift if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 8 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 8 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator>>") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2799,11 +2870,13 @@ class __operator_right_shift_equal if (__cached_size_ == -1) { if (__left_) - const_cast(__cached_size_) = __left_->size() + 9 + __right_->size(); + const_cast(__cached_size_) = static_cast( + __left_->size() + 9 + + __right_->size()); else const_cast(__cached_size_) = sizeof("operator>>=") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2849,11 +2922,11 @@ class __operator_sizeof_type if (__cached_size_ == -1) { if (__right_) - const_cast(__cached_size_) = __right_->size() + 9; + const_cast(__cached_size_) = static_cast(__right_->size() + 9); else const_cast(__cached_size_) = sizeof("operator sizeof") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2894,11 +2967,11 @@ class __operator_sizeof_expression if (__cached_size_ == -1) { if (__right_) - const_cast(__cached_size_) = __right_->size() + 9; + const_cast(__cached_size_) = static_cast(__right_->size() + 9); else const_cast(__cached_size_) = sizeof("operator sizeof") - 1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2936,8 +3009,8 @@ class __typeid virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __right_->size() + 8; - return __cached_size_; + const_cast(__cached_size_) = static_cast(__right_->size() + 8); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -2965,8 +3038,8 @@ class __throw virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __right_->size() + 6; - return __cached_size_; + const_cast(__cached_size_) = static_cast(__right_->size() + 6); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3005,8 +3078,8 @@ class __operator_sizeof_param_pack virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __right_->size() + 11; - return __cached_size_; + const_cast(__cached_size_) = static_cast(__right_->size() + 11); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3035,8 +3108,9 @@ class __const_cast virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 14 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 14 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3069,8 +3143,9 @@ class __dynamic_cast virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 16 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 16 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3103,8 +3178,9 @@ class __reinterpret_cast virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 20 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 20 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3137,8 +3213,9 @@ class __static_cast virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 15 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 15 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3175,9 +3252,9 @@ class __call_expr size_t off = __left_->size() + 2; if (__right_) off += __right_->size(); - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3210,8 +3287,9 @@ class __delete_array_expr virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = (__size_ ? 2 : 0) + 9 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast((__size_ ? 2 : 0) + + 9 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3242,8 +3320,9 @@ class __delete_expr virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = (__size_ ? 2 : 0) + 7 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast((__size_ ? 2 : 0) + + 7 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3298,9 +3377,9 @@ class __new_expr if (__right_) off += __right_->size(); } - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3360,8 +3439,9 @@ class __dot_star_expr virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 2 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 2 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3390,8 +3470,9 @@ class __dot_expr virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 1 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 1 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3419,8 +3500,9 @@ class __arrow_expr virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 2 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + 2 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3623,6 +3705,8 @@ class __sub } virtual bool is_function() const { + if (__left_ == 0) + return false; return __left_->is_function(); } virtual bool is_cv_qualifer() const @@ -3641,7 +3725,7 @@ class __sub { if (__left_ == 0) { - if (__size_ < t_end - t_begin) + if (__size_ < static_cast(t_end - t_begin)) { __left_ = t_begin[__size_]; __size_ = 0; @@ -3671,8 +3755,9 @@ class __unscoped_template_name virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3711,16 +3796,16 @@ class __list if (__left_ == NULL) const_cast(__cached_size_) = 0; else if (__right_ == NULL) - const_cast(__cached_size_) = __left_->size(); + const_cast(__cached_size_) = static_cast(__left_->size()); else { size_t off = __right_->size(); if (off > 0) off += 2; - const_cast(__cached_size_) = __left_->size() + off; + const_cast(__cached_size_) = static_cast(__left_->size() + off); } } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3742,10 +3827,14 @@ class __list } virtual bool ends_with_template(bool parsing = false) const { - if (__right_ != NULL) + if (__right_ && __right_->size() > 0) + { return __right_->ends_with_template(parsing); - if (__left_ != NULL) + } + else if (__left_ && __left_->size() > 0) + { return __left_->ends_with_template(parsing); + } return false; } virtual bool fix_forward_references(__node** t_begin, __node** t_end) @@ -3788,9 +3877,9 @@ class __template_args ++off; off += __right_->size(); } - const_cast(__cached_size_) = __left_->size() + off; + const_cast(__cached_size_) = static_cast(__left_->size() + off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3805,7 +3894,7 @@ class __template_args *buf++ = '>'; return buf; } - virtual bool ends_with_template(bool parsing = false) const + virtual bool ends_with_template(bool /*parsing*/ = false) const { return true; } @@ -3836,8 +3925,8 @@ class __function_args virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = 2 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(2 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -3852,6 +3941,91 @@ class __function_args } }; +class ___lambda_node + : public __node +{ +public: + ___lambda_node(__node* params, const char *number, size_t number_size) + { + __right_ = params; + __name_ = number; + __size_ = number_size; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t r = 2; + r += sizeof("'lambda'")-1; + if (__right_) + r += __right_->size(); + r += __size_; + const_cast(__cached_size_) = static_cast(r); + } + return static_cast(__cached_size_); + } + virtual char* first_demangled_name(char* buf) const + { + size_t n = sizeof("'lambda") - 1; + strncpy(buf, "'lambda", n); + buf += n; + if (__size_) + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + *buf++ = '\''; + *buf++ = '('; + if (__right_) + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__right_) + return __right_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __unnamed + : public __node +{ +public: + __unnamed(const char *number, size_t number_size) + { + __name_ = number; + __size_ = number_size; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t r = 0; + r += sizeof("'unnamed'")-1; + r += __size_; + const_cast(__cached_size_) = static_cast(r); + } + return static_cast(__cached_size_); + } + virtual char* first_demangled_name(char* buf) const + { + size_t n = sizeof("'unnamed") - 1; + strncpy(buf, "'unnamed", n); + buf += n; + if (__size_) + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + *buf++ = '\''; + return buf; + } +}; + class __cv_qualifiers : public __node { @@ -4229,9 +4403,9 @@ class __function off += 2; off += __right_->first_size(); } - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual bool is_function() const @@ -4478,8 +4652,8 @@ class __d_complex virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __left_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __left_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -4506,8 +4680,8 @@ class __imaginary virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = n + __left_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(n + __left_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -4563,9 +4737,9 @@ class __pack_expansion bottom->__left_ = sub; } } - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -4643,7 +4817,7 @@ class __wchar_t_literal explicit __wchar_t_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4715,7 +4889,7 @@ class __char_literal explicit __char_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4762,7 +4936,7 @@ class __signed_char_literal explicit __signed_char_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4809,7 +4983,7 @@ class __unsigned_char_literal explicit __unsigned_char_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4846,7 +5020,7 @@ class __short_literal explicit __short_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4893,7 +5067,7 @@ class __unsigned_short_literal explicit __unsigned_short_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4932,7 +5106,7 @@ class __int_literal explicit __int_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -4977,7 +5151,7 @@ class __unsigned_int_literal explicit __unsigned_int_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5014,7 +5188,7 @@ class __long_literal explicit __long_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5060,7 +5234,7 @@ class __unsigned_long_literal explicit __unsigned_long_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5098,7 +5272,7 @@ class __long_long_literal explicit __long_long_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5145,7 +5319,7 @@ class __unsigned_long_long_literal explicit __unsigned_long_long_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5163,7 +5337,7 @@ class __unsigned_long_long_literal } }; -class __int128 +class __signed_int128 : public __node { static const size_t n = sizeof("__int128") - 1; @@ -5184,7 +5358,7 @@ class __int128_literal explicit __int128_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5231,7 +5405,7 @@ class __unsigned_int128_literal explicit __unsigned_int128_literal(const char* __first, const char* __last) { __name_ = __first; - __size_ = __last - __first; + __size_ = static_cast(__last - __first); } virtual size_t first_size() const @@ -5264,14 +5438,14 @@ class __float_literal float v = static_cast(__value_); const_cast(__cached_size_) = sprintf(num, "%a", v)+1; } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { char num[20] = {0}; float v = static_cast(__value_); int n = sprintf(num, "%a", v); - strncpy(buf, num, n); + strncpy(buf, num, static_cast(n)); buf += n; *buf++ = 'f'; return buf; @@ -5309,14 +5483,14 @@ class __double_literal double v = static_cast(__value_); const_cast(__cached_size_) = sprintf(num, "%a", v); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { char num[30] = {0}; double v = static_cast(__value_); int n = sprintf(num, "%a", v); - strncpy(buf, num, n); + strncpy(buf, num, static_cast(n)); return buf + n; } }; @@ -5521,10 +5695,10 @@ class __array if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += snprintf(0, 0, "%ld", __size_); - const_cast(__cached_size_) = r; + r += static_cast(snprintf(0, 0, "%ld", __size_)); + const_cast(__cached_size_) = static_cast(r); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* get_demangled_name(char* buf) const @@ -5536,7 +5710,7 @@ class __array buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - size_t rs = sprintf(buf, "%ld", __size_); + int rs = sprintf(buf, "%ld", __size_); buf += rs; } *buf++ = ']'; @@ -5561,7 +5735,7 @@ class __array if (__right_ != 0) r += __right_->size(); else if (__size_ != 0) - r += snprintf(0, 0, "%ld", __size_); + r += static_cast(snprintf(0, 0, "%ld", __size_)); return r; } @@ -5573,7 +5747,7 @@ class __array buf = __right_->get_demangled_name(buf); else if (__size_ != 0) { - size_t off = sprintf(buf, "%ld", __size_); + int off = sprintf(buf, "%ld", __size_); buf += off; } char* t = buf; @@ -5610,10 +5784,10 @@ class __pointer_to_member_type virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + 3 - + __right_->first_size() - + __right_->second_size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + 3 + + __right_->first_size() + + __right_->second_size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -5652,8 +5826,8 @@ class __decltype_node virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = 10 + __right_->size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(10 + __right_->size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const @@ -5684,8 +5858,9 @@ class __nested_delimeter virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __left_->size() + __right_->size() + 2; - return __cached_size_; + const_cast(__cached_size_) = static_cast(__left_->size() + + __right_->size() + 2); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const @@ -5713,7 +5888,7 @@ class __nested_delimeter return __left_->fix_forward_references(t_begin, t_end) && __right_->fix_forward_references(t_begin, t_end); } - virtual __node* extract_cv(__node*& rt) const + virtual __node* extract_cv(__node*&) const { return __right_->extract_cv(const_cast<__node*&>(__right_)); } @@ -5740,9 +5915,10 @@ class __unresolved_name virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = (__left_ ? __left_->size() + 2 : 0) + - __right_->size() + __size_ * 2; - return __cached_size_; + const_cast(__cached_size_) = static_cast((__left_ ? + __left_->size() + 2 : 0) + + __right_->size() + __size_ * 2); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const @@ -5780,7 +5956,7 @@ class __unresolved_name r = __left_->fix_forward_references(t_begin, t_end); return r && __right_->fix_forward_references(t_begin, t_end); } - virtual __node* extract_cv(__node*& rt) const + virtual __node* extract_cv(__node*&) const { return __right_->extract_cv(const_cast<__node*&>(__right_)); } @@ -5816,8 +5992,8 @@ class __constructor virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __right_->base_size(); - return __cached_size_; + const_cast(__cached_size_) = static_cast(__right_->base_size()); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const @@ -5855,8 +6031,8 @@ class __destructor virtual size_t first_size() const { if (__cached_size_ == -1) - const_cast(__cached_size_) = __right_->base_size() + 1; - return __cached_size_; + const_cast(__cached_size_) = static_cast(__right_->base_size() + 1); + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const @@ -5882,7 +6058,7 @@ class __dot_suffix : public __node { public: - __dot_suffix(__node* name, const char* suffix, unsigned sz) + __dot_suffix(__node* name, const char* suffix, size_t sz) { __left_ = name; __name_ = suffix; @@ -5895,9 +6071,9 @@ class __dot_suffix { size_t off = __left_->size(); off += __size_ + 3; - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -5948,9 +6124,9 @@ class __vector_type off += __right_->size(); else if (__size_ > 0) off += __size_; - const_cast(__cached_size_) = off; + const_cast(__cached_size_) = static_cast(off); } - return __cached_size_; + return static_cast(__cached_size_); } virtual char* first_demangled_name(char* buf) const { @@ -6364,7 +6540,7 @@ __demangle_tree::__parse_builtin_type(const char* first, const char* last) ++first; break; case 'n': - if (__make<__int128>()) + if (__make<__signed_int128>()) ++first; break; case 'o': @@ -6444,6 +6620,7 @@ __demangle_tree::__parse_bare_function_type(const char* first, const char* last) { if (first != last) { + bool prev_tag_templates = __tag_templates_; __tag_templates_ = false; const char* t = __parse_type(first, last); if (t != first && __make<__list>(__root_)) @@ -6474,7 +6651,7 @@ __demangle_tree::__parse_bare_function_type(const char* first, const char* last) } } } - __tag_templates_ = true; + __tag_templates_ = prev_tag_templates; } return first; } @@ -6527,11 +6704,11 @@ __demangle_tree::__parse_hex_number(const char* first, const char* last, unsigne if (t == first) n = 0; if (isdigit(*t)) - n = n * 16 + *t - '0'; + n = n * 16 + static_cast(*t - '0'); else if (isupper(*t)) - n = n * 16 + *t - 'A' + 10; + n = n * 16 + static_cast(*t - 'A') + 10; else - n = n * 16 + *t - 'a' + 10; + n = n * 16 + static_cast(*t - 'a') + 10; } first = t; return first; @@ -6567,11 +6744,11 @@ __demangle_tree::__parse_expr_primary(const char* first, const char* last) switch (first[2]) { case '0': - if (__make<__bool_literal>("false", 5)) + if (__make<__bool_literal>("false", 5u)) first += 4; break; case '1': - if (__make<__bool_literal>("true", 4)) + if (__make<__bool_literal>("true", 4u)) first += 4; break; } @@ -6746,6 +6923,10 @@ __demangle_tree::__parse_expr_primary(const char* first, const char* last) first = t+1; } break; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + break; default: { // might be named type @@ -6792,12 +6973,67 @@ __demangle_tree::__parse_unnamed_type_name(const char* first, const char* last) { if (last - first > 2 && first[0] == 'U') { - switch (first[1]) + char type = first[1]; + switch (type) { case 't': case 'l': - first += 2; - __status_ = not_yet_implemented; + { + const char* t = first + 2; + __node* params = 0; + if (type == 'l') + { + if (*t == 'v') + { + // void lambda + ++t; + if (t != last && *t == 'E') + ++t; + else + return first; + } + else + { + const char* t1 = __parse_type(t, last); + if (t1 == t || !__make<__list>(__root_)) + return first; + params = __root_; + __node* prev = params; + t = t1; + while (true) + { + t1 = __parse_type(t, last); + if (t1 == t) + break; + if (!__make<__list>(__root_)) + return first; + t = t1; + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + prev = __root_; + } + if (t == last || *t != 'E') + return first; + ++t; + } + } + const char* number_start = t; + const char* number_end = __parse_number(t, last); + if (number_end == last || *number_end != '_') + return first; + t = number_end + 1; + if (type == 'l') + { + if (!__make<___lambda_node>(params, number_start, static_cast(number_end - number_start))) + return first; + } + else + { + if (!__make<__unnamed>(number_start, static_cast(number_end - number_start))) + return first; + } + first = t; + } break; } } @@ -6814,7 +7050,7 @@ __demangle_tree::__parse_unnamed_type_name(const char* first, const char* last) const char* __demangle_tree::__parse_ctor_dtor_name(const char* first, const char* last) { - if (last-first >= 2) + if (last-first >= 2 && __root_) { switch (first[0]) { @@ -6846,7 +7082,7 @@ __demangle_tree::__parse_ctor_dtor_name(const char* first, const char* last) } const char* -__demangle_tree::__parse_unscoped_template_name(const char* first, const char* last) +__demangle_tree::__parse_unscoped_template_name(const char* first, const char*) { // assert(!"__parse_unscoped_template_name not implemented"); __status_ = not_yet_implemented; @@ -7943,10 +8179,10 @@ __demangle_tree::__parse_array_type(const char* first, const char* last) } else if ('1' <= first[1] && first[1] <= '9') { - size_t dim = first[1] - '0'; + size_t dim = static_cast(first[1] - '0'); const char* t = first+2; for (; t != last && isdigit(*t); ++t) - dim = dim * 10 + *t - '0'; + dim = dim * 10 + static_cast(*t - '0'); if (t != last && *t == '_') { const char* t2 = __parse_type(t+1, last); @@ -8060,16 +8296,16 @@ __demangle_tree::__parse_template_param(const char* first, const char* last) else if (isdigit(first[1])) { const char* t = first+1; - size_t sub = *t - '0'; + size_t sub = static_cast(*t - '0'); for (++t; t != last && isdigit(*t); ++t) { sub *= 10; - sub += *t - '0'; + sub += static_cast(*t - '0'); } if (t == last || *t != '_') return first; ++sub; - if (sub < __t_end_ - __t_begin_) + if (sub < static_cast(__t_end_ - __t_begin_)) { if (__make<__sub>(__t_begin_[sub])) first = t+1; @@ -8109,7 +8345,7 @@ __demangle_tree::__parse_vector_type(const char* first, const char* last) return first; } const char* num = first + 2; - size_t sz = t - num; + size_t sz = static_cast(t - num); if (++t != last) { if (*t != 'p') @@ -8986,7 +9222,7 @@ __demangle_tree::__parse_operator_name(const char* first, const char* last, int* case 'v': // cast { - const char* t = __parse_type(first+2, last, false); + const char* t = __parse_type(first+2, last, false, true); if (t != first+2) { __node* cast_type = __root_; @@ -9991,14 +10227,14 @@ __demangle_tree::__parse_source_name(const char* first, const char* last) if ('1' <= c && c <= '9' && first+1 != last) { const char* t = first+1; - size_t n = c - '0'; + size_t n = static_cast(c - '0'); for (c = *t; '0' <= c && c <= '9'; c = *t) { - n = n * 10 + c - '0'; + n = n * 10 + static_cast(c - '0'); if (++t == last) return first; } - if (last - t >= n && __make<__source_name>(t, n)) + if (static_cast(last - t) >= n && __make<__source_name>(t, n)) first = t + n; } } @@ -10103,7 +10339,7 @@ __demangle_tree::__parse_nested_name(const char* first, const char* last) { bool can_sub = true; bool make_nested = true; - const char* t1; + const char* t1 = NULL; switch (*t0) { case '1': @@ -10179,8 +10415,9 @@ __demangle_tree::__parse_nested_name(const char* first, const char* last) } break; case 'U': - assert(!"__parse_nested_name U"); - // could have following + t1 = __parse_unnamed_type_name(t0, last); + if (t1 == t0 || t1 == last) + return first; break; case 'T': t1 = __parse_template_param(t0, last); @@ -10482,21 +10719,21 @@ __demangle_tree::__parse_substitution(const char* first, const char* last) size_t sub = 0; const char* t = first+1; if (isdigit(*t)) - sub = *t - '0'; + sub = static_cast(*t - '0'); else - sub = *t - 'A' + 10; + sub = static_cast(*t - 'A') + 10; for (++t; t != last && (isdigit(*t) || isupper(*t)); ++t) { sub *= 36; if (isdigit(*t)) - sub += *t - '0'; + sub += static_cast(*t - '0'); else - sub += *t - 'A' + 10; + sub += static_cast(*t - 'A') + 10; } if (t == last || *t != '_') return first; ++sub; - if (sub < __sub_end_ - __sub_begin_) + if (sub < static_cast(__sub_end_ - __sub_begin_)) { if (__make<__sub>(__sub_begin_[sub])) first = t+1; @@ -10593,7 +10830,7 @@ __demangle_tree::__parse_dot_suffix(const char* first, const char* last) { if (first != last && *first == '.') { - if (__make<__dot_suffix>(__root_, first, last-first)) + if (__make<__dot_suffix>(__root_, first, static_cast(last-first))) first = last; } return first; @@ -10616,6 +10853,7 @@ __demangle_tree::__parse_encoding(const char* first, const char* last) !name->is_ctor_dtor_conv(); __node* ret = NULL; const char* t2; + bool prev_tag_templates = __tag_templates_; __tag_templates_ = false; if (has_return) { @@ -10648,7 +10886,7 @@ __demangle_tree::__parse_encoding(const char* first, const char* last) } } } - __tag_templates_ = true; + __tag_templates_ = prev_tag_templates; } else first = t; @@ -10729,23 +10967,23 @@ printf("\n"); const unsigned N = 4096; char tmp[N]; ptrdiff_t s; - if (est <= bs) + if (static_cast(est) <= bs) { char* e = dmg_tree.__get_demangled_name(buf); *e++ = '\0'; s = e - buf; } - else if (est <= N) + else if (static_cast(est) <= N) { char* e = dmg_tree.__get_demangled_name(tmp); *e++ = '\0'; s = e - tmp; } else - s = dmg_tree.size() + 1; - if (s > bs) + s = static_cast(dmg_tree.size() + 1); + if (static_cast(s) > bs) { - buf = static_cast(realloc(buf, s)); + buf = static_cast(realloc(buf, static_cast(s))); if (buf == NULL) { if (status) @@ -10753,12 +10991,12 @@ printf("\n"); return NULL; } if (n) - *n = s; + *n = static_cast(s); } - if (est > bs) + if (static_cast(est) > bs) { - if (est <= N) - strncpy(buf, tmp, s); + if (static_cast(est) <= N) + strncpy(buf, tmp, static_cast(s)); else *dmg_tree.__get_demangled_name(buf) = '\0'; } @@ -10784,7 +11022,7 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) *status = __libcxxabi::invalid_args; return NULL; } - const size_t bs = 64 * 1024; + const size_t bs = 4 * 1024; __attribute((aligned(16))) char static_buf[bs]; buf = __libcxxabi::__demangle(__libcxxabi::__demangle(mangled_name, diff --git a/system/lib/libcxxabi/src/cxa_exception.cpp b/system/lib/libcxxabi/src/cxa_exception.cpp index b866f9e4df815..1c36299395b3e 100644 --- a/system/lib/libcxxabi/src/cxa_exception.cpp +++ b/system/lib/libcxxabi/src/cxa_exception.cpp @@ -466,9 +466,9 @@ __cxa_rethrow() globals->caughtExceptions = 0; } #if __arm__ - (void) _Unwind_SjLj_Resume_or_Rethrow(&exception_header->unwindHeader); + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); #else - (void)_Unwind_RaiseException(&exception_header->unwindHeader); + _Unwind_RaiseException(&exception_header->unwindHeader); #endif // If we get here, some kind of unwinding error has occurred. diff --git a/system/lib/libcxxabi/src/cxa_exception.hpp b/system/lib/libcxxabi/src/cxa_exception.hpp index cf019d4cd6bff..66f05c45f8736 100644 --- a/system/lib/libcxxabi/src/cxa_exception.hpp +++ b/system/lib/libcxxabi/src/cxa_exception.hpp @@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#ifndef _CXA_EXCEPTION_H +#define _CXA_EXCEPTION_H + #include // for std::unexpected_handler and std::terminate_handler #include #include "unwind.h" @@ -116,3 +119,5 @@ static const uint64_t get_vendor_and_language = 0xFFFFFFFFFFFFFF00; // mask #pragma GCC visibility pop } + +#endif // _CXA_EXCEPTION_H diff --git a/system/lib/libcxxabi/src/cxa_guard.cpp b/system/lib/libcxxabi/src/cxa_guard.cpp index 814aaeb18f0e5..e403b64abd5c8 100644 --- a/system/lib/libcxxabi/src/cxa_guard.cpp +++ b/system/lib/libcxxabi/src/cxa_guard.cpp @@ -28,7 +28,7 @@ namespace __cxxabiv1 namespace { -#if LIBCXXABI_ARMEABI +#if __arm__ // A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must // be statically initialized to 0. @@ -62,7 +62,7 @@ void set_initialized(guard_type* guard_object) { pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t guard_cv = PTHREAD_COND_INITIALIZER; -#if __APPLE__ +#if defined(__APPLE__) && !defined(__arm__) typedef uint32_t lock_type; @@ -100,7 +100,7 @@ set_lock(uint64_t& x, lock_type y) #endif // __LITTLE_ENDIAN__ -#else // __APPLE__ +#else // !__APPLE__ || __arm__ typedef bool lock_type; @@ -169,7 +169,7 @@ int __cxa_guard_acquire(guard_type* guard_object) int result = *initialized == 0; if (result) { -#if __APPLE__ +#if defined(__APPLE__) && !defined(__arm__) const lock_type id = pthread_mach_thread_np(pthread_self()); lock_type lock = get_lock(*guard_object); if (lock) @@ -189,14 +189,14 @@ int __cxa_guard_acquire(guard_type* guard_object) } else set_lock(*guard_object, id); -#else // __APPLE__ +#else // !__APPLE__ || __arm__ while (get_lock(*guard_object)) if (pthread_cond_wait(&guard_cv, &guard_mut)) abort_message("__cxa_guard_acquire condition variable wait failed"); result = *initialized == 0; if (result) set_lock(*guard_object, true); -#endif // __APPLE__ +#endif // !__APPLE__ || __arm__ } if (pthread_mutex_unlock(&guard_mut)) abort_message("__cxa_guard_acquire failed to release mutex"); diff --git a/system/lib/libcxxabi/src/cxa_handlers.cpp b/system/lib/libcxxabi/src/cxa_handlers.cpp index be43181c9e7f6..6c13fcd2c3cf3 100644 --- a/system/lib/libcxxabi/src/cxa_handlers.cpp +++ b/system/lib/libcxxabi/src/cxa_handlers.cpp @@ -22,85 +22,13 @@ namespace std { -static const char* cause = "uncaught"; - -static void default_terminate_handler() -{ - // If there might be an uncaught exception - using namespace __cxxabiv1; - __cxa_eh_globals* globals = __cxa_get_globals_fast(); - if (globals) - { - __cxa_exception* exception_header = globals->caughtExceptions; - // If there is an uncaught exception - if (exception_header) - { - _Unwind_Exception* unwind_exception = - reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; - bool native_exception = - (unwind_exception->exception_class & get_vendor_and_language) == - (kOurExceptionClass & get_vendor_and_language); - if (native_exception) - { - void* thrown_object = - unwind_exception->exception_class == kOurDependentExceptionClass ? - ((__cxa_dependent_exception*)exception_header)->primaryException : - exception_header + 1; - const __shim_type_info* thrown_type = - static_cast(exception_header->exceptionType); - // Try to get demangled name of thrown_type - int status; - char buf[1024]; - size_t len = sizeof(buf); - const char* name = __cxa_demangle(thrown_type->name(), buf, &len, &status); - if (status != 0) - name = thrown_type->name(); - // If the uncaught exception can be caught with std::exception& - const __shim_type_info* catch_type = - static_cast(&typeid(exception)); - if (catch_type->can_catch(thrown_type, thrown_object)) - { - // Include the what() message from the exception - const exception* e = static_cast(thrown_object); - abort_message("terminating with %s exception of type %s: %s", - cause, name, e->what()); - } - else - // Else just note that we're terminating with an exception - abort_message("terminating with %s exception of type %s", - cause, name); - } - else - // Else we're terminating with a foreign exception - abort_message("terminating with %s foreign exception", cause); - } - } - // Else just note that we're terminating - abort_message("terminating"); -} - -static void default_unexpected_handler() -{ - cause = "unexpected"; - terminate(); -} - -static terminate_handler __terminate_handler = default_terminate_handler; -static unexpected_handler __unexpected_handler = default_unexpected_handler; -static new_handler __new_handler = 0; - -unexpected_handler -set_unexpected(unexpected_handler func) _NOEXCEPT -{ - if (func == 0) - func = default_unexpected_handler; - return __sync_lock_test_and_set(&__unexpected_handler, func); -} - unexpected_handler get_unexpected() _NOEXCEPT { - return __sync_fetch_and_add(&__unexpected_handler, (unexpected_handler)0); + return __sync_fetch_and_add(&__cxa_unexpected_handler, (unexpected_handler)0); +// The above is safe but overkill on x86 +// Using of C++11 atomics this should be rewritten +// return __cxa_unexpected_handler.load(memory_order_acq); } __attribute__((visibility("hidden"), noreturn)) @@ -119,18 +47,13 @@ unexpected() __unexpected(get_unexpected()); } -terminate_handler -set_terminate(terminate_handler func) _NOEXCEPT -{ - if (func == 0) - func = default_terminate_handler; - return __sync_lock_test_and_set(&__terminate_handler, func); -} - terminate_handler get_terminate() _NOEXCEPT { - return __sync_fetch_and_add(&__terminate_handler, (terminate_handler)0); + return __sync_fetch_and_add(&__cxa_terminate_handler, (terminate_handler)0); +// The above is safe but overkill on x86 +// Using of C++11 atomics this should be rewritten +// return __cxa_terminate_handler.load(memory_order_acq); } __attribute__((visibility("hidden"), noreturn)) @@ -172,25 +95,31 @@ terminate() _NOEXCEPT (unwind_exception->exception_class & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language); if (native_exception) - { - __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; __terminate(exception_header->terminateHandler); - } } } __terminate(get_terminate()); } +extern "C" new_handler __cxa_new_handler = 0; +// In the future these will become: +// std::atomic __cxa_new_handler(0); + new_handler set_new_handler(new_handler handler) _NOEXCEPT { - return __sync_lock_test_and_set(&__new_handler, handler); + return __sync_swap(&__cxa_new_handler, handler); +// Using of C++11 atomics this should be rewritten +// return __cxa_new_handler.exchange(handler, memory_order_acq_rel); } new_handler get_new_handler() _NOEXCEPT { - return __sync_fetch_and_add(&__new_handler, (new_handler)0); + return __sync_fetch_and_add(&__cxa_new_handler, (new_handler)0); +// The above is safe but overkill on x86 +// Using of C++11 atomics this should be rewritten +// return __cxa_new_handler.load(memory_order_acq); } } // std diff --git a/system/lib/libcxxabi/src/cxa_handlers.hpp b/system/lib/libcxxabi/src/cxa_handlers.hpp index a7f582c008564..64994ac7b614c 100644 --- a/system/lib/libcxxabi/src/cxa_handlers.hpp +++ b/system/lib/libcxxabi/src/cxa_handlers.hpp @@ -10,6 +10,9 @@ // unexpected_handler, and new_handler. //===----------------------------------------------------------------------===// +#ifndef _CXA_HANDLERS_H +#define _CXA_HANDLERS_H + #include namespace std @@ -24,3 +27,28 @@ void __terminate(terminate_handler func) _NOEXCEPT; } // std + +extern "C" +{ + +extern void (*__cxa_terminate_handler)(); +extern void (*__cxa_unexpected_handler)(); +extern void (*__cxa_new_handler)(); + +/* + + At some point in the future these three symbols will become + C++11 atomic variables: + + extern std::atomic __cxa_terminate_handler; + extern std::atomic __cxa_unexpected_handler; + extern std::atomic __cxa_new_handler; + + This change will not impact their ABI. But it will allow for a + portible performance optimization. + +*/ + +} // extern "C" + +#endif // _CXA_HANDLERS_H diff --git a/system/lib/libcxxabi/src/cxa_personality.cpp b/system/lib/libcxxabi/src/cxa_personality.cpp index e49278c0ba69b..b2efe2aafab6b 100644 --- a/system/lib/libcxxabi/src/cxa_personality.cpp +++ b/system/lib/libcxxabi/src/cxa_personality.cpp @@ -48,19 +48,36 @@ | callSiteEncoding | (char) | Encoding for Call Site Table | +------------------+--+-----+-----+------------------------+--------------------------+ | callSiteTableLength | (ULEB128) | Call Site Table length, used to find Action table | -+---------------------+-----------+------------------------------------------------+--+ -| Beginning of Call Site Table If the current ip lies within the | ++---------------------+-----------+---------------------------------------------------+ +#if !__arm__ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip lies within the | | ... (start, length) range of one of these | -| call sites, there may be action needed. | +| call sites. There may be action needed. | | +-------------+---------------------------------+------------------------------+ | | | start | (encoded with callSiteEncoding) | offset relative to funcStart | | -| | length | (encoded with callSiteEncoding) | lenght of code fragment | | +| | length | (encoded with callSiteEncoding) | length of code fragment | | | | landingPad | (encoded with callSiteEncoding) | offset relative to lpStart | | | | actionEntry | (ULEB128) | Action Table Index 1-based | | | | | | actionEntry == 0 -> cleanup | | | +-------------+---------------------------------+------------------------------+ | | ... | -+---------------------------------------------------------------------+------------+ ++----------------------------------------------------------------------------------+ +#else // __arm_ ++---------------------+-----------+------------------------------------------------+ +| Beginning of Call Site Table The current ip is a 1-based index into | +| ... this table. Or it is -1 meaning no | +| action is needed. Or it is 0 meaning | +| terminate. | +| +-------------+---------------------------------+------------------------------+ | +| | landingPad | (ULEB128) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++----------------------------------------------------------------------------------+ +#endif // __arm_ ++---------------------------------------------------------------------+ | Beginning of Action Table ttypeIndex == 0 : cleanup | | ... ttypeIndex > 0 : catch | | ttypeIndex < 0 : exception spec | @@ -175,7 +192,7 @@ readULEB128(const uint8_t** data) /// @param data reference variable holding memory pointer to decode from /// @returns decoded value static -uintptr_t +intptr_t readSLEB128(const uint8_t** data) { uintptr_t result = 0; @@ -191,7 +208,7 @@ readSLEB128(const uint8_t** data) *data = p; if ((byte & 0x40) && (shift < (sizeof(result) << 3))) result |= static_cast(~0) << shift; - return result; + return static_cast(result); } /// Read a pointer encoded value and advance pointer @@ -219,7 +236,7 @@ readEncodedPointer(const uint8_t** data, uint8_t encoding) result = readULEB128(&p); break; case DW_EH_PE_sleb128: - result = readSLEB128(&p); + result = static_cast(readSLEB128(&p)); break; case DW_EH_PE_udata2: result = *((uint16_t*)p); @@ -230,19 +247,19 @@ readEncodedPointer(const uint8_t** data, uint8_t encoding) p += sizeof(uint32_t); break; case DW_EH_PE_udata8: - result = *((uint64_t*)p); + result = static_cast(*((uint64_t*)p)); p += sizeof(uint64_t); break; case DW_EH_PE_sdata2: - result = *((int16_t*)p); + result = static_cast(*((int16_t*)p)); p += sizeof(int16_t); break; case DW_EH_PE_sdata4: - result = *((int32_t*)p); + result = static_cast(*((int32_t*)p)); p += sizeof(int32_t); break; case DW_EH_PE_sdata8: - result = *((int64_t*)p); + result = static_cast(*((int64_t*)p)); p += sizeof(int64_t); break; default: @@ -292,7 +309,7 @@ call_terminate(bool native_exception, _Unwind_Exception* unwind_exception) static const __shim_type_info* -get_shim_type_info(int64_t ttypeIndex, const uint8_t* classInfo, +get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, bool native_exception, _Unwind_Exception* unwind_exception) { @@ -381,6 +398,41 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception) return adjustedPtr; } +namespace +{ + +struct scan_results +{ + int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup + const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. + const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected + uintptr_t landingPad; // null -> nothing found, else something found + void* adjustedPtr; // Used in cxa_exception.cpp + _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, + // _URC_FATAL_PHASE2_ERROR, + // _URC_CONTINUE_UNWIND, + // _URC_HANDLER_FOUND +}; + +} // unnamed namespace + +static +void +set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, + const scan_results& results) +{ +#if __arm__ + _Unwind_SetGR(context, 0, reinterpret_cast(unwind_exception)); + _Unwind_SetGR(context, 1, static_cast(results.ttypeIndex)); +#else + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), + reinterpret_cast(unwind_exception)); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), + static_cast(results.ttypeIndex)); +#endif + _Unwind_SetIP(context, results.landingPad); +} + /* There are 3 types of scans needed: @@ -402,24 +454,6 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception) _UA_CLEANUP_PHASE && !_UA_HANDLER_FRAME */ -namespace -{ - -struct scan_results -{ - int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup - const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. - const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected - uintptr_t landingPad; // null -> nothing found, else something found - void* adjustedPtr; // Used in cxa_exception.cpp - _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, - // _URC_FATAL_PHASE2_ERROR, - // _URC_CONTINUE_UNWIND, - // _URC_HANDLER_FOUND -}; - -} // unnamed namespace - static void scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception, @@ -477,7 +511,19 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception // Get beginning current frame's code (as defined by the // emitted dwarf code) uintptr_t funcStart = _Unwind_GetRegionStart(context); +#if __arm__ + if (ip == uintptr_t(-1)) + { + // no action + results.reason = _URC_CONTINUE_UNWIND; + return; + } + else if (ip == 0) + call_terminate(native_exception, unwind_exception); + // ip is 1-based index into call site table +#else // __arm__ uintptr_t ipOffset = ip - funcStart; +#endif // __arm__ const uint8_t* classInfo = NULL; // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding // dwarf emission @@ -498,14 +544,18 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception // Walk call-site table looking for range that // includes current PC. uint8_t callSiteEncoding = *lsda++; - uint32_t callSiteTableLength = readULEB128(&lsda); +#if __arm__ + (void)callSiteEncoding; // On arm callSiteEncoding is never used +#endif + uint32_t callSiteTableLength = static_cast(readULEB128(&lsda)); const uint8_t* callSiteTableStart = lsda; const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t* actionTableStart = callSiteTableEnd; const uint8_t* callSitePtr = callSiteTableStart; - while (true) + while (callSitePtr < callSiteTableEnd) { // There is one entry per call site. +#if !__arm__ // The call sites are non-overlapping in [start, start+length) // The call sites are ordered in increasing value of start uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); @@ -513,8 +563,15 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t actionEntry = readULEB128(&callSitePtr); if ((start <= ipOffset) && (ipOffset < (start + length))) +#else // __arm__ + // ip is 1-based index into this table + uintptr_t landingPad = readULEB128(&callSitePtr); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if (--ip == 0) +#endif // __arm__ { // Found the call site containing ip. +#if !__arm__ if (landingPad == 0) { // No handler here @@ -522,6 +579,9 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception return; } landingPad = (uintptr_t)lpStart + landingPad; +#else // __arm__ + ++landingPad; +#endif // __arm__ if (actionEntry == 0) { // Found a cleanup @@ -550,9 +610,9 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception // Found a catch, does it actually catch? // First check for catch (...) const __shim_type_info* catchType = - get_shim_type_info(ttypeIndex, classInfo, - ttypeEncoding, native_exception, - unwind_exception); + get_shim_type_info(static_cast(ttypeIndex), + classInfo, ttypeEncoding, + native_exception, unwind_exception); if (catchType == 0) { // Found catch (...) catches everything, including foreign exceptions @@ -713,6 +773,7 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception action += actionOffset; } // there is no break out of this loop, only return } +#if !__arm__ else if (ipOffset < start) { // There is no call site for this ip @@ -720,7 +781,12 @@ scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception // Possible stack corruption. call_terminate(native_exception, unwind_exception); } - } // there is no break out of this loop, only return +#endif // !__arm__ + } // there might be some tricky cases which break out of this loop + + // It is possible that no eh table entry specify how to handle + // this exception. By spec, terminate it immediately. + call_terminate(native_exception, unwind_exception); } // public API @@ -772,7 +838,12 @@ _UA_CLEANUP_PHASE */ _Unwind_Reason_Code -__gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, +#if __arm__ +__gxx_personality_sj0 +#else +__gxx_personality_v0 +#endif + (int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) { if (version != 1 || unwind_exception == 0 || context == 0) @@ -832,9 +903,7 @@ __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClas call_terminate(native_exception, unwind_exception); } // Jump to the handler - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), results.ttypeIndex); - _Unwind_SetIP(context, results.landingPad); + set_registers(unwind_exception, context, results); return _URC_INSTALL_CONTEXT; } // Either we didn't do a phase 1 search (due to forced unwinding), or @@ -844,9 +913,7 @@ __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClas if (results.reason == _URC_HANDLER_FOUND) { // Found a non-catching handler. Jump to it: - _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception); - _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), results.ttypeIndex); - _Unwind_SetIP(context, results.landingPad); + set_registers(unwind_exception, context, results); return _URC_INSTALL_CONTEXT; } // Did not find a cleanup. Return the results of the scan @@ -910,6 +977,7 @@ __cxa_call_unexpected(void* arg) // uint8_t ttypeEncoding uint8_t lpStartEncoding = *lsda++; const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + (void)lpStart; // purposefully unused. Just needed to increment lsda. uint8_t ttypeEncoding = *lsda++; if (ttypeEncoding == DW_EH_PE_omit) std::__terminate(t_handler); diff --git a/system/lib/libcxxabi/src/fallback_malloc.ipp b/system/lib/libcxxabi/src/fallback_malloc.ipp index 979f0bbd06516..e04fb1589a306 100644 --- a/system/lib/libcxxabi/src/fallback_malloc.ipp +++ b/system/lib/libcxxabi/src/fallback_malloc.ipp @@ -54,7 +54,7 @@ heap_node *node_from_offset ( const heap_offset offset ) { return (heap_node *) ( heap + ( offset * sizeof (heap_node))); } heap_offset offset_from_node ( const heap_node *ptr ) - { return (((char *) ptr ) - heap) / sizeof (heap_node); } + { return static_cast(static_cast(((char *) ptr ) - heap) / sizeof (heap_node)); } void init_heap () { freelist = (heap_node *) heap; @@ -87,7 +87,7 @@ void *fallback_malloc(size_t len) { p->len -= nelems; q = p + p->len; q->next_node = 0; - q->len = nelems; + q->len = static_cast(nelems); return (void *) (q + 1); } diff --git a/system/lib/libcxxabi/src/private_typeinfo.cpp b/system/lib/libcxxabi/src/private_typeinfo.cpp index 269116b66fa66..44b084aeaa03c 100644 --- a/system/lib/libcxxabi/src/private_typeinfo.cpp +++ b/system/lib/libcxxabi/src/private_typeinfo.cpp @@ -9,17 +9,73 @@ #include "private_typeinfo.h" +// The flag _LIBCXX_DYNAMIC_FALLBACK is used to make dynamic_cast more +// forgiving when type_info's mistakenly have hidden visibility and thus +// multiple type_infos can exist for a single type. +// +// When _LIBCXX_DYNAMIC_FALLBACK is defined, and only in the case where +// there is a detected inconsistency in the type_info hierarchy during a +// dynamic_cast, then the equality operation will fall back to using strcmp +// on type_info names to determin type_info equality. +// +// This change happens *only* under dynamic_cast, and only when +// dynamic_cast is faced with the choice: abort, or possibly give back the +// wrong answer. If when the dynamic_cast is done with this fallback +// algorithm and an inconsistency is still detected, dynamic_cast will call +// abort with an approriate message. +// +// The current implementation of _LIBCXX_DYNAMIC_FALLBACK requires a +// printf-like function called syslog: +// +// void syslog(const char* format, ...); +// +// If you want this functionality but your platform doesn't have syslog, +// just implement it in terms of fprintf(stderr, ...). +// +// _LIBCXX_DYNAMIC_FALLBACK is currently off by default. + +#if _LIBCXX_DYNAMIC_FALLBACK +#include "abort_message.h" +#include +#include +#endif + namespace __cxxabiv1 { #pragma GCC visibility push(hidden) +#if _LIBCXX_DYNAMIC_FALLBACK + +inline +bool +is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp) +{ + if (!use_strcmp) + return x == y; + return strcmp(x->name(), y->name()) == 0; +} + +#else // !_LIBCXX_DYNAMIC_FALLBACK + +inline +bool +is_equal(const std::type_info* x, const std::type_info* y, bool) +{ + return x == y; +} + +#endif // _LIBCXX_DYNAMIC_FALLBACK + // __shim_type_info __shim_type_info::~__shim_type_info() { } +void __shim_type_info::noop1() const {} +void __shim_type_info::noop2() const {} + // __fundamental_type_info // This miraculously (compiler magic) emits the type_info's for: @@ -116,12 +172,11 @@ bool __fundamental_type_info::can_catch(const __shim_type_info* thrown_type, void*&) const { - return this == thrown_type; + return is_equal(this, thrown_type, false); } bool -__array_type_info::can_catch(const __shim_type_info* thrown_type, - void*&) const +__array_type_info::can_catch(const __shim_type_info*, void*&) const { // We can get here if someone tries to catch an array by reference. // However if someone tries to throw an array, it immediately gets @@ -131,8 +186,7 @@ __array_type_info::can_catch(const __shim_type_info* thrown_type, } bool -__function_type_info::can_catch(const __shim_type_info* thrown_type, - void*&) const +__function_type_info::can_catch(const __shim_type_info*, void*&) const { // We can get here if someone tries to catch a function by reference. // However if someone tries to throw a function, it immediately gets @@ -146,16 +200,19 @@ bool __enum_type_info::can_catch(const __shim_type_info* thrown_type, void*&) const { - return this == thrown_type; + return is_equal(this, thrown_type, false); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + // Handles bullets 1 and 2 bool __class_type_info::can_catch(const __shim_type_info* thrown_type, void*& adjustedPtr) const { // bullet 1 - if (this == thrown_type) + if (is_equal(this, thrown_type, false)) return true; const __class_type_info* thrown_class_type = dynamic_cast(thrown_type); @@ -173,6 +230,8 @@ __class_type_info::can_catch(const __shim_type_info* thrown_type, return false; } +#pragma clang diagnostic pop + void __class_type_info::process_found_base_class(__dynamic_cast_info* info, void* adjustedPtr, @@ -206,7 +265,7 @@ __class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, void* adjustedPtr, int path_below) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, false)) process_found_base_class(info, adjustedPtr, path_below); } @@ -215,7 +274,7 @@ __si_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, void* adjustedPtr, int path_below) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, false)) process_found_base_class(info, adjustedPtr, path_below); else __base_type->has_unambiguous_public_base(info, adjustedPtr, path_below); @@ -244,7 +303,7 @@ __vmi_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, void* adjustedPtr, int path_below) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, false)) process_found_base_class(info, adjustedPtr, path_below); else { @@ -269,11 +328,14 @@ bool __pbase_type_info::can_catch(const __shim_type_info* thrown_type, void*&) const { - if (this == thrown_type) + if (is_equal(this, thrown_type, false)) return true; - return thrown_type == &typeid(std::nullptr_t); + return is_equal(thrown_type, &typeid(std::nullptr_t), false); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + // Handles bullets 1, 3 and 4 bool __pointer_type_info::can_catch(const __shim_type_info* thrown_type, @@ -292,10 +354,10 @@ __pointer_type_info::can_catch(const __shim_type_info* thrown_type, // bullet 3B if (thrown_pointer_type->__flags & ~__flags) return false; - if (__pointee == thrown_pointer_type->__pointee) + if (is_equal(__pointee, thrown_pointer_type->__pointee, false)) return true; // bullet 3A - if (__pointee == &typeid(void)) + if (is_equal(__pointee, &typeid(void), false)) return true; const __class_type_info* catch_class_type = dynamic_cast(__pointee); @@ -316,9 +378,14 @@ __pointer_type_info::can_catch(const __shim_type_info* thrown_type, return false; } +#pragma clang diagnostic pop + #pragma GCC visibility pop #pragma GCC visibility push(default) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-field-initializers" + // __dynamic_cast // static_ptr: pointer to an object of type static_type; nonnull, and since the @@ -417,12 +484,28 @@ __dynamic_cast(const void* static_ptr, __dynamic_cast_info info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; // Find out if we can use a giant short cut in the search - if (dynamic_type == dst_type) + if (is_equal(dynamic_type, dst_type, false)) { // Using giant short cut. Add that information to info. info.number_of_dst_type = 1; // Do the search - dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path); + dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, false); +#if _LIBCXX_DYNAMIC_FALLBACK + // The following if should always be false because we should definitely + // find (static_ptr, static_type), either on a public or private path + if (info.path_dst_ptr_to_static_ptr == unknown) + { + // We get here only if there is some kind of visibility problem + // in client code. + syslog(LOG_ERR, "dynamic_cast error 1: Both of the following type_info's " + "should have public visibility. At least of of them is hidden. %s" + ", %s.\n", static_type->name(), dynamic_type->name()); + // Redo the search comparing type_info's using strcmp + info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; + info.number_of_dst_type = 1; + dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, true); + } +#endif // _LIBCXX_DYNAMIC_FALLBACK // Query the search. if (info.path_dst_ptr_to_static_ptr == public_path) dst_ptr = dynamic_ptr; @@ -430,7 +513,22 @@ __dynamic_cast(const void* static_ptr, else { // Not using giant short cut. Do the search - dynamic_type->search_below_dst(&info, dynamic_ptr, public_path); + dynamic_type->search_below_dst(&info, dynamic_ptr, public_path, false); + #if _LIBCXX_DYNAMIC_FALLBACK + // The following if should always be false because we should definitely + // find (static_ptr, static_type), either on a public or private path + if (info.path_dst_ptr_to_static_ptr == unknown && + info.path_dynamic_ptr_to_static_ptr == unknown) + { + syslog(LOG_ERR, "dynamic_cast error 2: One or more of the following type_info's " + " has hidden visibility. They should all have public visibility. " + " %s, %s, %s.\n", static_type->name(), dynamic_type->name(), + dst_type->name()); + // Redo the search comparing type_info's using strcmp + info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; + dynamic_type->search_below_dst(&info, dynamic_ptr, public_path, true); + } +#endif // _LIBCXX_DYNAMIC_FALLBACK // Query the search. switch (info.number_to_static_ptr) { @@ -455,6 +553,8 @@ __dynamic_cast(const void* static_ptr, return const_cast(dst_ptr); } +#pragma clang diagnostic pop + #pragma GCC visibility pop #pragma GCC visibility push(hidden) @@ -584,12 +684,13 @@ __class_type_info::process_static_type_below_dst(__dynamic_cast_info* info, void __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { typedef const __base_class_type_info* Iter; - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -629,7 +730,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, current_ptr, current_ptr, public_path); + p->search_above_dst(info, current_ptr, current_ptr, public_path, use_strcmp); if (info->search_done) break; if (info->found_any_static_type) @@ -688,7 +789,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // This is not a static_type and not a dst_type. const Iter e = __base_info + __base_count; Iter p = __base_info; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); if (++p < e) { if ((__flags & __diamond_shaped_mask) || info->number_to_static_ptr == 1) @@ -701,7 +802,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, { if (info->search_done) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } else if (__flags & __non_diamond_repeat_mask) @@ -720,7 +821,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, if (info->number_to_static_ptr == 1 && info->path_dst_ptr_to_static_ptr == public_path) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } else @@ -743,7 +844,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // and not a dst_type under here. if (info->number_to_static_ptr == 1) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } } @@ -755,11 +856,12 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, void __si_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -787,7 +889,7 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - __base_type->search_above_dst(info, current_ptr, current_ptr, public_path); + __base_type->search_above_dst(info, current_ptr, current_ptr, public_path, use_strcmp); if (info->found_any_static_type) { is_dst_type_derived_from_static_type = true; @@ -822,7 +924,7 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, else { // This is not a static_type and not a dst_type - __base_type->search_below_dst(info, current_ptr, path_below); + __base_type->search_below_dst(info, current_ptr, path_below, use_strcmp); } } @@ -831,12 +933,13 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, void __class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { typedef const __base_class_type_info* Iter; - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -901,9 +1004,10 @@ void __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); else { @@ -926,7 +1030,7 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, dst_ptr, current_ptr, path_below); + p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); if (++p < e) { do @@ -955,7 +1059,7 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, dst_ptr, current_ptr, path_below); + p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); } while (++p < e); } // Restore flags @@ -970,12 +1074,13 @@ void __si_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); else - __base_type->search_above_dst(info, dst_ptr, current_ptr, path_below); + __base_type->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); } // This is the same algorithm as __vmi_class_type_info::search_above_dst but @@ -984,9 +1089,10 @@ void __class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); } @@ -998,7 +1104,8 @@ void __base_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; if (__offset_flags & __virtual_mask) @@ -1010,13 +1117,15 @@ __base_class_type_info::search_above_dst(__dynamic_cast_info* info, static_cast(current_ptr) + offset_to_base, (__offset_flags & __public_mask) ? path_below : - not_public_path); + not_public_path, + use_strcmp); } void __base_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; if (__offset_flags & __virtual_mask) @@ -1028,7 +1137,8 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info, static_cast(current_ptr) + offset_to_base, (__offset_flags & __public_mask) ? path_below : - not_public_path); + not_public_path, + use_strcmp); } #pragma GCC visibility pop diff --git a/system/lib/libcxxabi/src/private_typeinfo.h b/system/lib/libcxxabi/src/private_typeinfo.h index fec081ab8fc61..07e8ddea53337 100644 --- a/system/lib/libcxxabi/src/private_typeinfo.h +++ b/system/lib/libcxxabi/src/private_typeinfo.h @@ -18,13 +18,15 @@ namespace __cxxabiv1 #pragma GCC visibility push(hidden) -class __attribute__ ((__visibility__("hidden"))) __shim_type_info +class __attribute__ ((__visibility__("default"))) __shim_type_info : public std::type_info { public: - virtual ~__shim_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual ~__shim_type_info(); - virtual bool can_catch(const __shim_type_info* thrown_type, void*& adjustedPtr) const = 0; + __attribute__ ((__visibility__("hidden"))) virtual void noop1() const; + __attribute__ ((__visibility__("hidden"))) virtual void noop2() const; + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info* thrown_type, void*& adjustedPtr) const = 0; }; class __attribute__ ((__visibility__("default"))) __fundamental_type_info @@ -68,16 +70,16 @@ enum no }; -class __class_type_info; +class __attribute__ ((__visibility__("default"))) __class_type_info; struct __dynamic_cast_info { // const data supplied to the search: - const __class_type_info* const dst_type; - const void* const static_ptr; - const __class_type_info* const static_type; - const std::ptrdiff_t src2dst_offset; + const __class_type_info* dst_type; + const void* static_ptr; + const __class_type_info* static_type; + std::ptrdiff_t src2dst_offset; // Data that represents the answer: @@ -131,9 +133,9 @@ class __attribute__ ((__visibility__("default"))) __class_type_info __attribute__ ((__visibility__("hidden"))) void process_found_base_class(__dynamic_cast_info*, void*, int) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; __attribute__ ((__visibility__("hidden"))) @@ -150,9 +152,9 @@ class __attribute__ ((__visibility__("default"))) __si_class_type_info __attribute__ ((__visibility__("hidden"))) virtual ~__si_class_type_info(); __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; @@ -170,8 +172,8 @@ struct __base_class_type_info __offset_shift = 8 }; - void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; - void search_below_dst(__dynamic_cast_info*, const void*, int) const; + void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; + void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; @@ -195,9 +197,9 @@ class __attribute__ ((__visibility__("default"))) __vmi_class_type_info __attribute__ ((__visibility__("hidden"))) virtual ~__vmi_class_type_info(); __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; diff --git a/system/lib/libcxxabi/src/stdexcept.cpp b/system/lib/libcxxabi/src/stdexcept.cpp index 4aa962d0a2045..de859db458119 100644 --- a/system/lib/libcxxabi/src/stdexcept.cpp +++ b/system/lib/libcxxabi/src/stdexcept.cpp @@ -14,6 +14,11 @@ #include #include +#if __APPLE__ +#include +#include +#endif + // Note: optimize for size #pragma GCC visibility push(hidden) @@ -26,13 +31,39 @@ class __libcpp_nmstr private: const char* str_; - typedef std::size_t unused_t; - typedef std::int32_t count_t; - - static const std::ptrdiff_t offset = static_cast(2*sizeof(unused_t) + - sizeof(count_t)); + typedef int count_t; + + struct _Rep_base + { + std::size_t len; + std::size_t cap; + count_t count; + }; + + static const std::ptrdiff_t offset = static_cast(sizeof(_Rep_base)); + + count_t& count() const _NOEXCEPT {return ((_Rep_base*)(str_ - offset))->count;} + +#if __APPLE__ + static + const void* + compute_gcc_empty_string_storage() _LIBCPP_CANTTHROW + { + void* handle = dlopen("/usr/lib/libstdc++.6.dylib", RTLD_NOLOAD); + if (handle == 0) + return 0; + return (const char*)dlsym(handle, "_ZNSs4_Rep20_S_empty_rep_storageE") + offset; + } + + static + const void* + get_gcc_empty_string_storage() _LIBCPP_CANTTHROW + { + static const void* p = compute_gcc_empty_string_storage(); + return p; + } +#endif - count_t& count() const _NOEXCEPT {return (count_t&)(*(str_ - sizeof(count_t)));} public: explicit __libcpp_nmstr(const char* msg); __libcpp_nmstr(const __libcpp_nmstr& s) _LIBCPP_CANTTHROW; @@ -44,9 +75,9 @@ class __libcpp_nmstr __libcpp_nmstr::__libcpp_nmstr(const char* msg) { std::size_t len = strlen(msg); - str_ = new char[len + 1 + offset]; - unused_t* c = (unused_t*)str_; - c[0] = c[1] = len; + str_ = static_cast(::operator new(len + 1 + offset)); + _Rep_base* c = (_Rep_base*)str_; + c->len = c->cap = len; str_ += offset; count() = 0; std::strcpy(const_cast(c_str()), msg); @@ -56,7 +87,10 @@ inline __libcpp_nmstr::__libcpp_nmstr(const __libcpp_nmstr& s) : str_(s.str_) { - __sync_add_and_fetch(&count(), 1); +#if __APPLE__ + if (str_ != get_gcc_empty_string_storage()) +#endif + __sync_add_and_fetch(&count(), 1); } __libcpp_nmstr& @@ -64,17 +98,30 @@ __libcpp_nmstr::operator=(const __libcpp_nmstr& s) { const char* p = str_; str_ = s.str_; - __sync_add_and_fetch(&count(), 1); - if (__sync_add_and_fetch((count_t*)(p-sizeof(count_t)), -1) < 0) - delete [] (p-offset); +#if __APPLE__ + if (str_ != get_gcc_empty_string_storage()) +#endif + __sync_add_and_fetch(&count(), 1); +#if __APPLE__ + if (p != get_gcc_empty_string_storage()) +#endif + if (__sync_add_and_fetch((count_t*)(p-sizeof(count_t)), count_t(-1)) < 0) + { + ::operator delete(const_cast(p-offset)); + } return *this; } inline __libcpp_nmstr::~__libcpp_nmstr() { - if (__sync_add_and_fetch(&count(), -1) < 0) - delete [] (str_ - offset); +#if __APPLE__ + if (str_ != get_gcc_empty_string_storage()) +#endif + if (__sync_add_and_fetch(&count(), count_t(-1)) < 0) + { + ::operator delete(const_cast(str_ - offset)); + } } } diff --git a/system/lib/libcxxabi/src/temporary.cpp b/system/lib/libcxxabi/src/temporary.cpp deleted file mode 100644 index 5facf39de26a9..0000000000000 --- a/system/lib/libcxxabi/src/temporary.cpp +++ /dev/null @@ -1,41 +0,0 @@ -//===---------------------------- temporary.cpp ---------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "abort_message.h" - -#pragma GCC visibility push(default) - -extern "C" -{ - -static -void f1() -{ - abort_message("__cxa_new_handler shouldn't be called"); -} - -static -void f2() -{ - abort_message("__cxa_terminate_handler shouldn't be called"); -} - -static -void f3() -{ - abort_message("__cxa_unexpected_handler shouldn't be called"); -} - -void (*__cxa_new_handler)() = f1; -void (*__cxa_terminate_handler)() = f2; -void (*__cxa_unexpected_handler)() = f3; - -} - -#pragma GCC visibility pop diff --git a/tests/cases/fp80.ll b/tests/cases/fp80.ll new file mode 100644 index 0000000000000..7fc0db4a07f9f --- /dev/null +++ b/tests/cases/fp80.ll @@ -0,0 +1,21 @@ +; ModuleID = 'src.cpp.o' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00" ; [#uses=1] + +; [#uses=0] +define i32 @main() { +entry: + %x = zext i32 0 to x86_fp80 + %1 = bitcast x86_fp80 %x to i80 + %2 = trunc i80 %1 to i32 + %retval = alloca i32, align 4 ; [#uses=1] + store i32 0, i32* %retval + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0] + ret i32 0 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) + diff --git a/tests/cases/gepoverflow.txt b/tests/cases/gepoverflow.txt index 90772c33db3eb..e01c78dd19d20 100644 --- a/tests/cases/gepoverflow.txt +++ b/tests/cases/gepoverflow.txt @@ -1,2 +1,2 @@ -*5246502,5247072* +*5246470,5247040* *-514,56* diff --git a/tests/cases/subnums.ll b/tests/cases/subnums.ll index 981a1592ec71c..dc2af1c7295ea 100644 --- a/tests/cases/subnums.ll +++ b/tests/cases/subnums.ll @@ -2,7 +2,7 @@ target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" target triple = "i386-pc-linux-gnu" -@.str = private unnamed_addr constant [15 x i8] c"hello, world! %d\0A\00", align 1 ; [#uses=1 type=[18 x i8]*] +@.str = private unnamed_addr constant [18 x i8] c"hello, world! %d\0A\00", align 1 ; [#uses=1 type=[18 x i8]*] ; [#uses=0] define i32 @main() { diff --git a/tests/cmake/target_html/CMakeLists.txt b/tests/cmake/target_html/CMakeLists.txt new file mode 100644 index 0000000000000..9f891e718937a --- /dev/null +++ b/tests/cmake/target_html/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8) + +project(hello_world_gles.html) + +file(GLOB sourceFiles ../../hello_world_gles.c) + +if (CMAKE_BUILD_TYPE STREQUAL Debug) + SET(linkFlags "") +else() # Either MinSizeRel, RelWithDebInfo or Release, all which run with optimizations enabled. + SET(linkFlags "-O2") +endif() + +add_executable(hello_world_gles.html ${sourceFiles}) +set_target_properties(hello_world_gles.html PROPERTIES LINK_FLAGS "${linkFlags}") diff --git a/tests/cmake/target_js/CMakeLists.txt b/tests/cmake/target_js/CMakeLists.txt new file mode 100644 index 0000000000000..860b70a9f3452 --- /dev/null +++ b/tests/cmake/target_js/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8) + +project(hello_world.js) + +file(GLOB sourceFiles ../../hello_world.cpp) + +if (CMAKE_BUILD_TYPE STREQUAL Debug) + SET(linkFlags "") +else() # Either MinSizeRel, RelWithDebInfo or Release, all which run with optimizations enabled. + SET(linkFlags "-O2") +endif() + +add_executable(hello_world.js ${sourceFiles}) +set_target_properties(hello_world.js PROPERTIES LINK_FLAGS "${linkFlags}") diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index e7b4d9852f5c3..dc052d1af9295 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -96,6 +96,10 @@ class ValHolder { this->v = v; } + int returnIntPlusFive( int x ) { + return x + 5; + } + static int some_class_method(int i) { return i; } @@ -286,6 +290,7 @@ EMSCRIPTEN_BINDINGS(([]() { .constructor() .method("getVal", &ValHolder::getVal) .method("setVal", &ValHolder::setVal) + .method("returnIntPlusFive", &ValHolder::returnIntPlusFive) .classmethod("some_class_method", &ValHolder::some_class_method) ; function("emval_test_return_ValHolder", &emval_test_return_ValHolder); diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js index e01f02362ecd9..8c61553bcff83 100644 --- a/tests/embind/embind_test.js +++ b/tests/embind/embind_test.js @@ -137,6 +137,8 @@ module({ c.setVal('1234'); assert.equal('1234', c.getVal()); + assert.equal(1239, c.returnIntPlusFive(1234)); + c.delete(); assert.equal(0, cm.count_emval_handles()); }, diff --git a/tests/freetype/src/tools/chktrcmp.py b/tests/freetype/src/tools/chktrcmp.py index d0f342e6bdd79..33213d2dc70af 100755 --- a/tests/freetype/src/tools/chktrcmp.py +++ b/tests/freetype/src/tools/chktrcmp.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # Check trace components in FreeType 2 source. # Author: suzuki toshiya, 2009 diff --git a/tests/freetype/src/tools/docmaker/docbeauty.py b/tests/freetype/src/tools/docmaker/docbeauty.py index 3ddf4a94a1551..8718a4849f4ce 100644 --- a/tests/freetype/src/tools/docmaker/docbeauty.py +++ b/tests/freetype/src/tools/docmaker/docbeauty.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # DocBeauty (c) 2003, 2004, 2008 David Turner # diff --git a/tests/freetype/src/tools/docmaker/docmaker.py b/tests/freetype/src/tools/docmaker/docmaker.py index 1d9de9fbff260..278742fabdb79 100644 --- a/tests/freetype/src/tools/docmaker/docmaker.py +++ b/tests/freetype/src/tools/docmaker/docmaker.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # DocMaker (c) 2002, 2004, 2008 David Turner # diff --git a/tests/freetype/src/tools/glnames.py b/tests/freetype/src/tools/glnames.py index 55573b22fe80b..173a8b42d8450 100644 --- a/tests/freetype/src/tools/glnames.py +++ b/tests/freetype/src/tools/glnames.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # diff --git a/tests/gl_subdata.cpp b/tests/gl_subdata.cpp new file mode 100644 index 0000000000000..d159b2b2da0bb --- /dev/null +++ b/tests/gl_subdata.cpp @@ -0,0 +1,141 @@ +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#include +extern "C" { +#include +#include +} +static const char vertex_shader[] = + "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "attribute float indices;\n" + "uniform sampler2D nodeInfo;\n" + "varying vec4 color;" + "\n" + "void main(void)\n" + "{\n" + " float s = (indices + 0.5) / 512.; \n" + " vec4 v = texture2D(nodeInfo, vec2( s, 0.5));\n" + " gl_Position = vec4(v.x, v.y, 0.5, 1.);\n" + " gl_PointSize = v.z;\n" + " color = vec4(0.5 + v.w/2., 0.5 + 0.5 * v.w/2., 0.5, 1);\n" + "}\n"; +static const char fragment_shader[] = + "#ifdef GL_ES\n" + "precision highp float;\n" + "#endif\n" + "\n" + "varying vec4 color;\n" + "void main(void)\n" + "{\n" + " float dst = distance(vec2(0.5, 0.5), gl_PointCoord); \n" + " gl_FragColor = color;\n" + " if ( dst > 0.3) {" + " gl_FragColor = vec4(0., 0., 0.5, 0.2);\n" + "}\n" + "if ( dst > 0.5) discard;\n" + "}"; +struct NodeInfo { //structure that we want to transmit to our shaders + float x; + float y; + float s; + float c; +}; +GLuint nodeTexture; //texture id used to bind +GLuint nodeSamplerLocation; //shader sampler address +GLuint indicesAttributeLocation; //shader attribute address +GLuint indicesVBO; //Vertex Buffer Object Id; +const int nbNodes = 512; +NodeInfo data[nbNodes]; //our data that will be transmitted using float texture. +double alpha = 0; //use to make a simple funny effect; +static void updateFloatTexture() { + int count = 0; + for (float x=0; x < nbNodes; ++x ) { + data[count].x = 0.2*pow(cos(alpha), 3) + (sin(alpha)*3. + 3.5) * x/nbNodes * cos(alpha + x/nbNodes * 16. * M_PI); + data[count].y = 0.2*pow(sin(alpha), 3) + (sin(alpha)*3. + 3.5) * x/nbNodes * sin(alpha + x/nbNodes * 16. * M_PI); + data[count].s = (16. + 16. * cos(alpha + x/nbNodes * 32. * M_PI)) + 8.;// * fmod(x/nbNodes + alpha, 1.) + 5.; + data[count].c = 0.5 + 0.5 * sin(alpha + x/nbNodes * 32. * M_PI); + ++count; + } + glBindTexture(GL_TEXTURE_2D, nodeTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, nbNodes, 1, 0, GL_RGBA, GL_FLOAT, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, NULL); + alpha -= 0.001; +} +static void glut_draw_callback(void) { + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(1., 1., 1., 0.); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glActiveTexture(GL_TEXTURE0); + updateFloatTexture(); //we change the texture each time to create the effect (it is just for the test) + glBindTexture(GL_TEXTURE_2D, nodeTexture); + glUniform1i(nodeSamplerLocation, GL_TEXTURE0); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, indicesVBO); + glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, NULL); + glDrawArrays(GL_POINTS, 0, nbNodes); + glutSwapBuffers(); +} +GLuint createShader(const char source[], int type) { + char msg[512]; + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, (const GLchar**)(&source), NULL); + glCompileShader(shader); + glGetShaderInfoLog(shader, sizeof msg, NULL, msg); + std::cout << "Shader info: " << msg << std::endl; + return shader; +} +static void gl_init(void) { + GLuint program = glCreateProgram(); + glAttachShader(program, createShader(vertex_shader , GL_VERTEX_SHADER)); + glAttachShader(program, createShader(fragment_shader, GL_FRAGMENT_SHADER)); + glLinkProgram(program); + char msg[512]; + glGetProgramInfoLog(program, sizeof msg, NULL, msg); + std::cout << "info: " << msg << std::endl; + glUseProgram(program); + std::vector elements(nbNodes); + int count = 0; + for (float x=0; x < nbNodes; ++x ) { + elements[count] = count; + ++count; + } + /*Create one texture to store all the needed information */ + glGenTextures(1, &nodeTexture); + /* Store the vertices in a vertex buffer object (VBO) */ + glGenBuffers(1, &indicesVBO); + glBindBuffer(GL_ARRAY_BUFFER, indicesVBO); + float zeroes[nbNodes]; + memset(zeroes, 0, sizeof(zeroes)); + glBufferData(GL_ARRAY_BUFFER, elements.size() * sizeof(float), zeroes, GL_STATIC_DRAW); + for (int x = 0; x < nbNodes; x++) { + glBufferSubData(GL_ARRAY_BUFFER, x * sizeof(float), elements.size() * sizeof(float), &elements[x]); + } + /* Get the locations of the uniforms so we can access them */ + nodeSamplerLocation = glGetUniformLocation(program, "nodeInfo"); + glBindAttribLocation(program, 0, "indices"); + //Enable glPoint size in shader, always enable in Open Gl ES 2. + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_POINT_SPRITE); +} +int main(int argc, char *argv[]) { + glutInit(&argc, argv); + glutInitWindowSize(640, 480); + glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); + glutCreateWindow("Simple FLOAT Texture Test"); + /* Set up glut callback functions */ + glutDisplayFunc(glut_draw_callback ); + gl_init(); + glutMainLoop(); + return 0; +} + + diff --git a/tests/hello_libcxx_mod1.cpp b/tests/hello_libcxx_mod1.cpp new file mode 100644 index 0000000000000..2389b1efc0258 --- /dev/null +++ b/tests/hello_libcxx_mod1.cpp @@ -0,0 +1,9 @@ +#include + +int main() +{ + std::cout << "hello, world!" << std::endl; + std::cout << "hello, world!" << std::endl; + return 0; +} + diff --git a/tests/python/ccproxy.py b/tests/python/ccproxy.py index 82750096646a3..98cc8c41dd91f 100644 --- a/tests/python/ccproxy.py +++ b/tests/python/ccproxy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 from __future__ import with_statement diff --git a/tests/runner.py b/tests/runner.py index 7523036b6cf8e..b30489891fb42 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # This Python file uses the following encoding: utf-8 ''' @@ -94,6 +94,12 @@ def setUp(self): os.makedirs(dirname) self.working_dir = dirname os.chdir(dirname) + + if not self.save_dir: + self.has_prev_ll = False + for temp_file in os.listdir(TEMP_DIR): + if temp_file.endswith('.ll'): + self.has_prev_ll = True def tearDown(self): if self.save_JS: @@ -107,6 +113,12 @@ def tearDown(self): os.chdir(os.path.join(self.get_dir(), '..')) shutil.rmtree(self.get_dir()) + # Make sure we don't leave stuff around + if not self.has_prev_ll: + for temp_file in os.listdir(TEMP_DIR): + assert not temp_file.endswith('.ll'), temp_file + # TODO assert not temp_file.startswith('emscripten_'), temp_file + def skip(self, why): print >> sys.stderr, ' ' % why, @@ -187,7 +199,7 @@ def run_post(post): process(sys.argv[1]) ''') transform.close() - transform_args = ['--js-transform', "python %s" % transform_filename] + transform_args = ['--js-transform', "%s %s" % (PYTHON, transform_filename)] Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args + transform_args + Building.COMPILER_TEST_OPTS, filename + '.o.js') run_post(post2) @@ -267,10 +279,18 @@ def run_generated_code(self, engine, filename, args=[], check_timeout=True): return ret def build_native(self, filename): - Popen([CLANG, '-O2', filename, '-o', filename+'.native'], stdout=PIPE).communicate()[0] + process = Popen([CLANG, '-O2', '-fno-math-errno', filename, '-o', filename+'.native'], stdout=PIPE); + output = process.communicate() + if process.returncode is not 0: + print >> sys.stderr, "Building native executable with command '%s' failed with a return code %d!" % (' '.join([CLANG, '-O2', filename, '-o', filename+'.native']), process.returncode) + print "Output: " + output[0] def run_native(self, filename, args): - Popen([filename+'.native'] + args, stdout=PIPE).communicate()[0] + process = Popen([filename+'.native'] + args, stdout=PIPE); + output = process.communicate() + if process.returncode is not 0: + print >> sys.stderr, "Running native executable with command '%s' failed with a return code %d!" % (' '.join([filename+'.native'] + args), process.returncode) + print "Output: " + output[0] def assertIdentical(self, values, y): if type(values) not in [list, tuple]: values = [values] @@ -958,7 +978,7 @@ def test_i64_precise(self): Settings.PRECISE_I64_MATH = 0 self.do_run(src, 'unsigned') code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - assert 'goog.math.Long' not in code and 'jsbn' not in code, 'i64 precise math should not have been included if not asked for' + assert 'goog.math.Long' not in code, 'i64 precise math should not have been included if not asked for' # Verify that even if we ask for precision, if it is not needed it is not included Settings.PRECISE_I64_MATH = 1 @@ -982,7 +1002,28 @@ def test_i64_precise(self): ''' self.do_run(src, '*4903566027370624, 153236438355333*') code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() - assert 'goog.math.Long' not in code and 'jsbn' not in code, 'i64 precise math should not have been included if not actually used' + assert 'goog.math.Long' not in code, 'i64 precise math should not have been included if not actually used' + + # But if we force it to be included, it is. First, a case where we don't need it + Settings.PRECISE_I64_MATH = 2 + self.do_run(open(path_from_root('tests', 'hello_world.c')).read(), 'hello') + code = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() + assert 'goog.math.Long' in code, 'i64 precise math should be included if forced' + + # and now one where we do + self.do_run(r''' + #include + + int main( int argc, char ** argv ) + { + unsigned long a = 0x60DD1695U; + unsigned long b = 0xCA8C4E7BU; + unsigned long long c = (unsigned long long)a * b; + printf( "c = %016llx\n", c ); + + return 0; + } + ''', 'c = 4ca38a6bd2973f97') def test_i64_zextneg(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') @@ -1042,6 +1083,32 @@ def test_i64_i16(self): ''' self.do_run(src, '<=0') + def test_i64_qdouble(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include + typedef long long qint64; /* 64 bit signed */ + typedef double qreal; + + + int main(int argc, char **argv) + { + qreal c = 111; + qint64 d = -111 + (argc - 1); + c += d; + if (c < -1 || c > 1) + { + printf("Failed!\n"); + } + else + { + printf("Succeeded!\n"); + } + }; + ''' + self.do_run(src, 'Succeeded!') + def test_i32_mul_precise(self): if self.emcc_args == None: return self.skip('needs ta2') @@ -1335,6 +1402,7 @@ def test_bitfields(self): def test_floatvars(self): src = ''' #include + #include int main() { float x = 1.234, y = 3.5, q = 0.00000001; @@ -1342,6 +1410,8 @@ def test_floatvars(self): int z = x < y; printf("*%d,%d,%.1f,%d,%.4f,%.2f*\\n", z, int(y), y, (int)x, x, q); + printf("%.2f, %.2f, %.2f, %.2f\\n", fmin(0.5, 3.3), fmin(NAN, 3.3), fmax(0.5, 3.3), fmax(NAN, 3.3)); + /* // Rounding behavior float fs[6] = { -2.75, -2.50, -2.25, 2.25, 2.50, 2.75 }; @@ -1353,7 +1423,7 @@ def test_floatvars(self): return 0; } ''' - self.do_run(src, '*1,10,10.5,1,1.2340,0.00*') + self.do_run(src, '*1,10,10.5,1,1.2340,0.00*\n0.50, 3.30, 3.30, 3.30\n') def test_globaldoubles(self): src = r''' @@ -1417,6 +1487,24 @@ def test_math(self): ''' self.do_run(src, '*3.14,-3.14,1,0,0,0,1,0,1,1,0,2,3,0.0,1.0,0.0,1.0*') + def test_erf(self): + src = ''' + #include + #include + int main() + { + printf("%1.6f, %1.6f, %1.6f, %1.6f, %1.6f, %1.6f\\n", + erf(1.0), + erf(3.0), + erf(-1.0), + erfc(1.0), + erfc(3.0), + erfc(-1.5)); + return 0; + } + ''' + self.do_run(src, '0.842701, 0.999978, -0.842701, 0.157299, 0.000022, 1.966105') + def test_math_hyperbolic(self): src = open(path_from_root('tests', 'hyperbolic', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'hyperbolic', 'output.txt'), 'r').read() @@ -1591,7 +1679,15 @@ def test_strings(self): return 0; } ''' - self.do_run(src, '4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\n', ['wowie', 'too', '74']) + for named, expected in [(0, range(100, 200)), (1, [0])]: + print named + Settings.NAMED_GLOBALS = named + self.do_run(src, '4:10,177,543,def\n4\nwowie\ntoo\n76\n5\n(null)\n/* a comment */\n// another\ntest\n', ['wowie', 'too', '74']) + if self.emcc_args == []: + gen = open(self.in_dir('src.cpp.o.js')).read() + count = gen.count('GLOBAL_BASE') + assert count in expected, count + print ' counted' def test_strcmp_uni(self): src = ''' @@ -1602,7 +1698,7 @@ def test_strcmp_uni(self): #define TEST(func) \ { \ char *word = "WORD"; \ - char *wordEntry = "Â"; \ + char wordEntry[2] = { -61,-126 }; /* "Â"; */ \ int cmp = func(word, wordEntry, 2); \ printf("Compare value " #func " is %d\\n", cmp); \ } @@ -1881,7 +1977,7 @@ def test_sup(self): TEST(C4__); TEST(C4_2); TEST(C__z); - return 1; + return 0; } ''' if Settings.QUANTUM_SIZE == 1: @@ -1896,7 +1992,7 @@ def test_assert(self): int main() { assert(1 == true); // pass assert(1 == false); // fail - return 1; + return 0; } ''' self.do_run(src, 'Assertion failed: 1 == false') @@ -1936,6 +2032,155 @@ def test_longjmp(self): else: self.do_run(src, 'second\nmain: 0\n') + def test_longjmp2(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void stack_manipulate_func(jmp_state* s, int level) { + jmp_buf buf; + + printf("Entering stack_manipulate_func, level: %d\n", level); + + if (level == 0) { + s->jmp = &buf; + if (setjmp(*(s->jmp)) == 0) { + printf("Setjmp normal execution path, level: %d\n", level); + stack_manipulate_func(s, level + 1); + } else { + printf("Setjmp error execution path, level: %d\n", level); + } + } else { + printf("Perform longjmp at level %d\n", level); + longjmp(*(s->jmp), 1); + } + + printf("Exiting stack_manipulate_func, level: %d\n", level); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + stack_manipulate_func(&s, 0); + + return 0; + } + ''' + self.do_run(src, '''Entering stack_manipulate_func, level: 0 +Setjmp normal execution path, level: 0 +Entering stack_manipulate_func, level: 1 +Perform longjmp at level 1 +Setjmp error execution path, level: 0 +Exiting stack_manipulate_func, level: 0 +''') + + def test_longjmp3(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void setjmp_func(jmp_state* s, int level) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + + if (level == 2) { + printf("level is 2, perform longjmp!\n"); + longjmp(*(s->jmp), 1); + } + + if (setjmp(c_jmp) == 0) { + printf("setjmp normal execution path, level: %d\n", level); + s->jmp = &c_jmp; + setjmp_func(s, level + 1); + } else { + printf("setjmp exception execution path, level: %d\n", level); + if (prev_jmp) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + longjmp(*(s->jmp), 1); + } + } + + printf("Exiting setjmp function, level: %d\n", level); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + setjmp_func(&s, 0); + + return 0; + } + ''' + self.do_run(src, '''setjmp normal execution path, level: 0 +setjmp normal execution path, level: 1 +level is 2, perform longjmp! +setjmp exception execution path, level: 1 +prev_jmp is not empty, continue with longjmp! +setjmp exception execution path, level: 0 +Exiting setjmp function, level: 0 +''') + + def test_longjmp4(self): + src = r''' + #include + #include + + typedef struct { + jmp_buf* jmp; + } jmp_state; + + void second_func(jmp_state* s); + + void first_func(jmp_state* s) { + jmp_buf* prev_jmp = s->jmp; + jmp_buf c_jmp; + volatile int once = 0; + + if (setjmp(c_jmp) == 0) { + printf("Normal execution path of first function!\n"); + + s->jmp = &c_jmp; + second_func(s); + } else { + printf("Exception execution path of first function! %d\n", once); + + if (!once) { + printf("Calling longjmp the second time!\n"); + once = 1; + longjmp(*(s->jmp), 1); + } + } + } + + void second_func(jmp_state* s) { + longjmp(*(s->jmp), 1); + } + + int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = NULL; + + first_func(&s); + + return 0; + } + ''' + self.do_run(src, '''Normal execution path of first function! +Exception execution path of first function! 0 +Calling longjmp the second time! +Exception execution path of first function! 1 +''') + def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") @@ -1968,7 +2213,7 @@ def test_exceptions(self): } catch(...) { printf("done!*\\n"); } - return 1; + return 0; } ''' self.do_run(src, '*throw...caught!infunc...done!*') @@ -2075,6 +2320,69 @@ def test_typed_exceptions(self): expected = open(path_from_root('tests', 'exceptions', 'output.txt'), 'r').read() self.do_run(src, expected) + def test_multiexception(self): + Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.EXCEPTION_DEBUG = 0 # Messes up expected output. + src = r''' +#include + +static int current_exception_id = 0; + +typedef struct { + int jmp; +} jmp_state; + +void setjmp_func(jmp_state* s, int level) { + int prev_jmp = s->jmp; + int c_jmp; + + if (level == 2) { + printf("level is 2, perform longjmp!\n"); + throw 1; + } + + c_jmp = current_exception_id++; + try { + printf("setjmp normal execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + s->jmp = c_jmp; + setjmp_func(s, level + 1); + } catch (int catched_eid) { + printf("caught %d\n", catched_eid); + if (catched_eid == c_jmp) { + printf("setjmp exception execution path, level: %d, prev_jmp: %d\n", level, prev_jmp); + if (prev_jmp != -1) { + printf("prev_jmp is not empty, continue with longjmp!\n"); + s->jmp = prev_jmp; + throw s->jmp; + } + } else { + throw; + } + } + + printf("Exiting setjmp function, level: %d, prev_jmp: %d\n", level, prev_jmp); +} + +int main(int argc, char *argv[]) { + jmp_state s; + s.jmp = -1; + + setjmp_func(&s, 0); + + return 0; +} +''' + self.do_run(src, '''setjmp normal execution path, level: 0, prev_jmp: -1 +setjmp normal execution path, level: 1, prev_jmp: 0 +level is 2, perform longjmp! +caught 1 +setjmp exception execution path, level: 1, prev_jmp: 0 +prev_jmp is not empty, continue with longjmp! +caught 0 +setjmp exception execution path, level: 0, prev_jmp: -1 +Exiting setjmp function, level: 0, prev_jmp: -1 +''') + def test_class(self): src = ''' #include @@ -2400,7 +2708,7 @@ def test_stack_byval(self): } } printf("sum:%d*\n", total); - return 1; + return 0; } ''' self.do_run(src, 'sum:9780*') @@ -2726,6 +3034,7 @@ def test_emscripten_api(self): // EMSCRIPTEN_COMMENT("hello from the source"); emscripten_run_script("Module.print('hello world' + '!')"); printf("*%d*\n", emscripten_run_script_int("5*20")); + printf("*%s*\n", emscripten_run_script_string("'five'+'six'")); emscripten_run_script("_save_me_aimee()"); return 0; } @@ -2737,19 +3046,26 @@ def process(filename): # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src ''' - self.do_run(src, 'hello world!\n*100*\nmann\n', post_build=check) + self.do_run(src, 'hello world!\n*100*\n*fivesix*\nmann\n', post_build=check) def test_inlinejs(self): src = r''' #include + double get() { + double ret = 0; + __asm __volatile__("12/3.3":"=a"(ret)); + return ret; + } + int main() { asm("Module.print('Inline JS is very cool')"); + printf("%.2f\n", get()); return 0; } ''' - self.do_run(src, 'Inline JS is very cool') + self.do_run(src, 'Inline JS is very cool\n3.64') def test_memorygrowth(self): if Settings.USE_TYPED_ARRAYS == 0: return self.skip('memory growth is only supported with typed arrays') @@ -2898,7 +3214,7 @@ def test_indirectbr(self): FOO: printf("bad\\n"); - return 1; + return 0; BAR: printf("good\\n"); const void *addr = &&FOO; @@ -3012,6 +3328,78 @@ def test_varargs(self): ''' self.do_run(src, '*cheez: 0+24*\n*cheez: 0+24*\n*albeit*\n*albeit*\nQ85*\nmaxxi:21*\nmaxxD:22.10*\n*vfp:22,199*\n*vfp:22,199*\n') + def test_varargs_byval(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('FIXME: Add support for this') + + src = r''' + #include + #include + + typedef struct type_a { + union { + double f; + void *p; + int i; + short sym; + } value; + } type_a; + + enum mrb_vtype { + MRB_TT_FALSE = 0, /* 0 */ + MRB_TT_CLASS = 9 /* 9 */ + }; + + typedef struct type_b { + enum mrb_vtype tt:8; + } type_b; + + void print_type_a(int argc, ...); + void print_type_b(int argc, ...); + + int main(int argc, char *argv[]) + { + type_a a; + type_b b; + a.value.p = (void*) 0x12345678; + b.tt = MRB_TT_CLASS; + + printf("The original address of a is: %p\n", a.value.p); + printf("The original type of b is: %d\n", b.tt); + + print_type_a(1, a); + print_type_b(1, b); + + return 0; + } + + void print_type_a(int argc, ...) { + va_list ap; + type_a a; + + va_start(ap, argc); + a = va_arg(ap, type_a); + va_end(ap); + + printf("The current address of a is: %p\n", a.value.p); + } + + void print_type_b(int argc, ...) { + va_list ap; + type_b b; + + va_start(ap, argc); + b = va_arg(ap, type_b); + va_end(ap); + + printf("The current type of b is: %d\n", b.tt); + } + ''' + self.do_run(src, '''The original address of a is: 0x12345678 +The original type of b is: 9 +The current address of a is: 0x12345678 +The current type of b is: 9 +''') + def test_structbyval(self): # part 1: make sure that normally, passing structs by value works @@ -3111,7 +3499,7 @@ def test_structbyval(self): Building.link([supp_name + '.o', main_name + '.o'], all_name) # This will fail! See explanation near the warning we check for, in the compiler source code - output = Popen(['python', EMCC, all_name], stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, all_name], stderr=PIPE).communicate() # Check for warning in the generated code generated = open(os.path.join(self.get_dir(), 'src.cpp.o.js')).read() assert 'Casting a function pointer type to another with a different number of arguments' in output[1], 'Missing expected warning' @@ -3217,6 +3605,25 @@ def test_time(self): extra_emscripten_args=['-H', 'libc/time.h']) #extra_emscripten_args=['-H', 'libc/fcntl.h,libc/sys/unistd.h,poll.h,libc/math.h,libc/langinfo.h,libc/time.h']) + def test_timeb(self): + # Confirms they are called in reverse order + src = r''' + #include + #include + #include + + int main() { + timeb tb; + tb.timezone = 1; + printf("*%d\n", ftime(&tb)); + assert(tb.time > 10000); + assert(tb.timezone == 0); + assert(tb.dstflag == 0); + return 0; + } + ''' + self.do_run(src, '*0\n') + def test_intentional_fault(self): # Some programs intentionally segfault themselves, we should compile that into a throw src = r''' @@ -3524,6 +3931,7 @@ def test_nestedstructs(self): def test_runtimelink(self): if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize printf into puts in the parent, and the child will still look for puts') + if Settings.NAMED_GLOBALS == 0: return self.skip('dlopen cannot work without named globals, TODO') main, supp = self.setup_runtimelink_test() @@ -3683,6 +4091,7 @@ def process(filename): def test_dlfcn_data_and_fptr(self): if Building.LLVM_OPTS: return self.skip('LLVM opts will optimize out parent_func') + if Settings.NAMED_GLOBALS == 0: return self.skip('dlopen cannot work without named globals, TODO') Settings.LINKABLE = 1 @@ -3787,6 +4196,7 @@ def test_dlfcn_alias(self): Settings.LINKABLE = 1 if Building.LLVM_OPTS == 2: return self.skip('LLVM LTO will optimize away stuff we expect from the shared library') + if Settings.NAMED_GLOBALS == 0: return self.skip('dlopen cannot work without named globals, TODO') lib_src = r''' #include @@ -4024,7 +4434,7 @@ def test_strtok(self): printf("at %s:%s\n", word, phrase); } } - return 1; + return 0; } ''' @@ -4138,6 +4548,19 @@ def test_vprintf(self): ''' self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected)) + def test_snprintf0(self): + src = r''' + #include + int main() { + int size = snprintf(NULL, 0, "%s %d %.2f\n", "me and myself", 25, 1.345); + char buf[size]; + snprintf(buf, size, "%s %d %.2f\n", "me and myself", 25, 1.345); + printf("%d : %s\n", size, buf); + return 0; + } + ''' + self.do_run(src, '22 : me and myself 25 1.34\n') + def test_atoX(self): if self.emcc_args is None: return self.skip('requires ta2') @@ -4383,11 +4806,15 @@ def test_sscanf_n(self): int x = sscanf("one %n two", "%s %n", word, &l); printf("%d,%s,%d\n", x, word, l); - + { + int a, b, c, count; + count = sscanf("12345 6789", "%d %n%d", &a, &b, &c); + printf("%i %i %i %i\n", count, a, b, c); + } return 0; } ''' - self.do_run(src, '''[DEBUG] word 1: version, l: 7\n1,one,4''') + self.do_run(src, '''[DEBUG] word 1: version, l: 7\n1,one,4\n2 12345 6 6789\n''') def test_sscanf_whitespace(self): src = r''' @@ -4437,7 +4864,22 @@ def test_sscanf_3(self): self.do_run(src, '3\n123,1073741823,1125899906842620\n' + '3\n-123,-1073741823,-1125899906842620\n') - + + def test_sscanf_4(self): + src = r''' + #include + + int main() + { + char pYear[16], pMonth[16], pDay[16], pDate[64]; + printf("%d\n", sscanf("Nov 19 2012", "%s%s%s", pMonth, pDay, pYear)); + printf("day %s, month %s, year %s \n", pDay, pMonth, pYear); + return(0); + } + ''' + + self.do_run(src, '3\nday 19, month Nov, year 2012'); + def test_langinfo(self): src = open(path_from_root('tests', 'langinfo', 'test.c'), 'r').read() expected = open(path_from_root('tests', 'langinfo', 'output.txt'), 'r').read() @@ -5271,7 +5713,7 @@ def test_gethostbyname(self): test("www.cheezburger.com"); test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips test("localhost"); - return 1; + return 0; } ''' self.do_run(src, '''www.cheezburger.com : 1 : 4 @@ -5531,7 +5973,7 @@ def test_dlmalloc(self): # emcc should build in dlmalloc automatically, and do all the sign correction etc. for it try_delete(os.path.join(self.get_dir(), 'src.cpp.o.js')) - output = Popen(['python', EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=100000000', + output = Popen([PYTHON, EMCC, path_from_root('tests', 'dlmalloc_test.c'), '-s', 'TOTAL_MEMORY=100000000', '-o', os.path.join(self.get_dir(), 'src.cpp.o.js')], stdout=PIPE, stderr=self.stderr_redirect).communicate() self.do_run('x', '*1,0*', ['200', '1'], no_build=True) @@ -5548,6 +5990,39 @@ def test_dlmalloc(self): ]: self.do_run(src.replace('{{{ NEW }}}', new).replace('{{{ DELETE }}}', delete), '*1,0*') + def test_dlmalloc_partial(self): + if self.emcc_args is None: return self.skip('only emcc will link in dlmalloc') + # present part of the symbols of dlmalloc, not all + src = open(path_from_root('tests', 'new.cpp')).read().replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' +void * +operator new(size_t size) +{ + printf("new %d!\\n", size); + return malloc(size); +} +''' + self.do_run(src, 'new 4!\n*1,0*') + + def test_dlmalloc_partial_2(self): + if self.emcc_args is None or 'SAFE_HEAP' in str(self.emcc_args): return self.skip('only emcc will link in dlmalloc, and we do unsafe stuff') + # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. + src = r''' + #include + #include + void *malloc(size_t size) + { + return (void*)123; + } + int main() { + void *x = malloc(10); + printf("got %p\n", x); + free(x); + printf("freed the faker\n"); + return 1; + } +''' + self.do_run(src, 'got 0x7b\nfreed') + def test_libcxx(self): self.do_run(open(path_from_root('tests', 'hashtest.cpp')).read(), 'june -> 30\nPrevious (in alphabetical order) is july\nNext (in alphabetical order) is march') @@ -5559,7 +6034,7 @@ def test_libcxx(self): std::set *fetchOriginatorNums = new std::set(); fetchOriginatorNums->insert(171); printf("hello world\\n"); - return 1; + return 0; } ''', 'hello world'); @@ -5736,7 +6211,11 @@ def test_cubescript(self): self.do_run(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') def test_gcc_unmangler(self): - self.do_run(path_from_root('third_party'), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') + Settings.NAMED_GLOBALS = 0 # test coverage for this + + Building.COMPILER_TEST_OPTS = ['-I' + path_from_root('third_party')] + + self.do_run(open(path_from_root('third_party', 'gcc_demangler.c')).read(), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj']) #### Code snippet that is helpful to search for nonportable optimizations #### #global LLVM_OPT_OPTS @@ -6042,8 +6521,12 @@ def do_test(): finally: del os.environ['EMCC_DEBUG'] for debug in [1,2]: - self.assertIdentical(open('release.js').read().replace('\n\n', '\n').replace('\n\n', '\n'), open('debug%d.js' % debug).read().replace('\n\n', '\n').replace('\n\n', '\n')) # EMCC_DEBUG=1 mode must not generate different code! + def clean(text): + return text.replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n') + self.assertIdentical(clean(open('release.js').read()), clean(open('debug%d.js' % debug).read())) # EMCC_DEBUG=1 mode must not generate different code! print >> sys.stderr, 'debug check %d passed too' % debug + + try_delete(CANONICAL_TEMP_DIR) else: print >> sys.stderr, 'not doing debug check' @@ -6116,7 +6599,7 @@ def test_cases(self): # Autodebug the code def do_autodebug(self, filename): - output = Popen(['python', AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] assert 'Success.' in output, output self.prep_ll_run(filename, filename+'.o.ll.ll', force_recompile=True) # rebuild .bc # TODO: use code in do_autodebug_post for this @@ -6128,7 +6611,7 @@ def do_autodebug_post(self, filename): return True print 'Autodebugging during post time' delattr(self, 'post') - output = Popen(['python', AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + output = Popen([PYTHON, AUTODEBUGGER, filename+'.o.ll', filename+'.o.ll.ll'], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] assert 'Success.' in output, output shutil.copyfile(filename + '.o.ll.ll', filename + '.o.ll') Building.llvm_as(filename) @@ -6309,8 +6792,8 @@ def test_scriptaclass(self): ''' post = ''' def process(filename): - Popen(['python', DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() - Popen(['python', NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() + Popen([PYTHON, DEMANGLER, filename], stdout=open(filename + '.tmp', 'w')).communicate() + Popen([PYTHON, NAMESPACER, filename, filename + '.tmp'], stdout=open(filename + '.tmp2', 'w')).communicate() src = open(filename, 'r').read().replace( '// {{MODULE_ADDITIONS}', 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().replace('var ModuleNames = ', '').rstrip() + ';\n\n' + script_src + '\n\n' + @@ -6365,7 +6848,7 @@ class Child2 : public Parent { open(header_filename, 'w').write(header) basename = os.path.join(self.get_dir(), 'bindingtest') - output = Popen(['python', BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + output = Popen([PYTHON, BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] #print output assert 'Traceback' not in output, 'Failure in binding generation: ' + output @@ -6538,7 +7021,7 @@ class StringUser { open(header_filename, 'w').write(header) basename = os.path.join(self.get_dir(), 'bindingtest') - output = Popen(['python', BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + output = Popen([PYTHON, BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] #print output assert 'Traceback' not in output, 'Failure in binding generation: ' + output @@ -7173,6 +7656,7 @@ def setUp(self): Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 Settings.PRECISE_I64_MATH = 0 + Settings.NAMED_GLOBALS = 0 if not (embetter and llvm_opts) else 1 Building.pick_llvm_opts(3) @@ -7212,7 +7696,7 @@ def test_emcc(self): suffix = '.c' if compiler == EMCC else '.cpp' # --version - output = Popen(['python', compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('''emcc (Emscripten GCC-like replacement) 2.0 Copyright (C) 2012 the Emscripten authors. This is free and open source software under the MIT license. @@ -7220,11 +7704,11 @@ def test_emcc(self): ''', output[0].replace('\r', ''), output[1].replace('\r', '')) # -v, without input files - output = Popen(['python', compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('''clang version''', output[1].replace('\r', ''), output[1].replace('\r', '')) # --help - output = Popen(['python', compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('''%s [options] file... Most normal gcc/g++ options will work, for example: @@ -7237,7 +7721,7 @@ def test_emcc(self): # emcc src.cpp ==> writes a.out.js self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('a.out.js'), '\n'.join(output) self.assertContained('hello, world!', run_js('a.out.js')) @@ -7245,44 +7729,46 @@ def test_emcc(self): # properly report source code errors, and stop there self.clear() assert not os.path.exists('a.out.js') - output = Popen(['python', compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE).communicate() + process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE) + output = process.communicate() assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected' assert len(output[0]) == 0, output[0] + assert process.returncode is not 0, 'Failed compilation must return a nonzero error code!' self.assertNotContained('IOError', output[1]) # no python stack self.assertNotContained('Traceback', output[1]) # no python stack self.assertContained('error: invalid preprocessing directive', output[1]) - self.assertContained("error: use of undeclared identifier 'cheez", output[1]) - self.assertContained('2 errors generated', output[1]) - assert 'emcc: compiler frontend failed to generate LLVM bitcode, halting' in output[1].split('2 errors generated.')[1] + self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], output[1]) + self.assertContained('errors generated', output[1]) + assert 'emcc: compiler frontend failed to generate LLVM bitcode, halting' in output[1].split('errors generated.')[1] # emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file # regression check: -o js should create "js", with bitcode content for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]: target = args[1] if len(args) == 2 else 'hello_world.o' self.clear() - Popen(['python', compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() + Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() syms = Building.llvm_nm(target) assert len(syms.defs) == 1 and 'main' in syms.defs, 'Failed to generate valid bitcode' if target == 'js': # make sure emcc can recognize the target as a bitcode file shutil.move(target, target + '.bc') target += '.bc' - output = Popen(['python', compiler, target, '-o', target + '.js'], stdout = PIPE, stderr = PIPE).communicate() + output = Popen([PYTHON, compiler, target, '-o', target + '.js'], stdout = PIPE, stderr = PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output)) self.assertContained('hello, world!', run_js(target + '.js')) # handle singleton archives self.clear() - Popen(['python', compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists('a.a') - output = Popen(['python', compiler, 'a.a']).communicate() + output = Popen([PYTHON, compiler, 'a.a']).communicate() assert os.path.exists('a.out.js'), output self.assertContained('hello, world!', run_js('a.out.js')) # emcc src.ll ==> generates .js self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('a.out.js'), '\n'.join(output) self.assertContained('hello, world!', run_js('a.out.js')) @@ -7294,7 +7780,7 @@ def test_emcc(self): os.mkdir('b_dir') for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]: self.clear(in_curr=True) - output = Popen(['python', compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.ll'), '-o', path], stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists(path), path + ' does not exist; ' + '\n'.join(output) self.assertContained('hello, world!', run_js(path)) finally: @@ -7308,7 +7794,7 @@ def test_emcc(self): # very speed-sensitive. So we do not implement it in JS in library.js, instead we compile it from source for source, has_malloc in [('hello_world' + suffix, False), ('hello_malloc.cpp', True)]: self.clear() - output = Popen(['python', compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', source)], stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists('a.out.js'), '\n'.join(output) self.assertContained('hello, world!', run_js('a.out.js')) generated = open('a.out.js').read() @@ -7334,14 +7820,14 @@ def test_emcc(self): ]: #print params, opt_level, bc_params, closure self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params, + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] if bc_params is not None: if '-O1' in params and 'something.bc' in params: assert 'warning: -Ox flags ignored, since not generating JavaScript' in output[1] assert os.path.exists('something.bc'), output[1] - output = Popen(['python', compiler, 'something.bc', '-o', 'something.js'] + bc_params, stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists('something.js'), output[1] assert ('Warning: Applying some potentially unsafe optimizations!' in output[1]) == (opt_level >= 3), 'unsafe warning should appear in opt >= 3' self.assertContained('hello, world!', run_js('something.js')) @@ -7360,9 +7846,9 @@ def test_emcc(self): # XXX find a way to test this: assert ('& 255' in generated or '&255' in generated) == (opt_level <= 2), 'corrections should be in opt <= 2' assert ('(label)' in generated) == (opt_level <= 1), 'relooping should be in opt >= 2' assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0' - assert 'var $i;' in generated or 'var $i_01;' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on' + assert 'var $i;' in generated or 'var $i_0' in generated or 'var $storemerge3;' in generated or 'var $storemerge4;' in generated or 'var $i_04;' in generated, 'micro opts should always be on' if opt_level >= 2: - assert 'HEAP8[HEAP32[' in generated or 'HEAP8[$vla1 + (($storemerge4 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + (($storemerge4 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + (($i_04 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + ($i_04 / 2 & -1)]' in generated or 'HEAP8[$1 + (($i_01 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$1 + (($i_01 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$1 + ($i_01 / 2 & -1)]' in generated, 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 + assert 'HEAP8[$0 + ($i_' in generated or 'HEAP8[$0 + (($i_0' in generated or 'HEAP8[HEAP32[' in generated or 'HEAP8[$vla1 + (($storemerge4 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + (($storemerge4 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + (($i_04 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$vla1 + ($i_04 / 2 & -1)]' in generated or 'HEAP8[$1 + (($i_01 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$1 + (($i_01 | 0) / 2 & -1) | 0]' in generated or 'HEAP8[$1 + ($i_01 / 2 & -1)]' in generated, 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2 assert ('_puts(' in generated) == (opt_level >= 1), 'with opt >= 1, llvm opts are run and they should optimize printf to puts' assert ('function _malloc(bytes) {' in generated) == (not has_malloc), 'If malloc is needed, it should be there, if not not' assert 'function _main() {' in generated, 'Should be unminified, including whitespace' @@ -7382,7 +7868,7 @@ def test_emcc(self): (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), ]: self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js'] + params, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('a.out.js'), '\n'.join(output) self.assertContained('hello, world!', run_js('a.out.js')) @@ -7391,7 +7877,7 @@ def test_emcc(self): # Compiling two source files into a final JS. for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]: self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args, + output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args, stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists(target), '\n'.join(output) @@ -7399,7 +7885,7 @@ def test_emcc(self): # Compiling two files with -c will generate separate .bc files self.clear() - output = Popen(['python', compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args, + output = Popen([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args, stdout=PIPE, stderr=PIPE).communicate() if '-o' in args: # specifying -o and -c is an error @@ -7411,24 +7897,24 @@ def test_emcc(self): assert not os.path.exists(target), 'We should only have created bitcode here: ' + '\n'.join(output) # Compiling one of them alone is expected to fail - output = Popen(['python', compiler, 'twopart_main.o'] + args, stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, 'twopart_main.o'] + args, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists(target), '\n'.join(output) #print '\n'.join(output) self.assertContained('is not a function', run_js(target, stderr=STDOUT)) try_delete(target) # Combining those bc files into js should work - output = Popen(['python', compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE).communicate() assert os.path.exists(target), '\n'.join(output) self.assertContained('side got: hello from main, over', run_js(target)) # Combining bc files into another bc should also work try_delete(target) assert not os.path.exists(target) - output = Popen(['python', compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.bc'] + args, stdout=PIPE, stderr=PIPE).communicate() syms = Building.llvm_nm('combined.bc') assert len(syms.defs) == 2 and 'main' in syms.defs, 'Failed to generate valid bitcode' - output = Popen(['python', compiler, 'combined.bc', '-o', 'combined.bc.js'], stdout = PIPE, stderr = PIPE).communicate() + output = Popen([PYTHON, compiler, 'combined.bc', '-o', 'combined.bc.js'], stdout = PIPE, stderr = PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('combined.bc.js'), 'Expected %s to exist' % ('combined.bc.js') self.assertContained('side got: hello from main, over', run_js('combined.bc.js')) @@ -7444,16 +7930,80 @@ def test_emcc(self): f.close() ''') trans_file.close() - output = Popen(['python', compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', 'python t.py'], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', '%s t.py' % (PYTHON)], stdout=PIPE, stderr=PIPE).communicate() assert open('a.out.js').read() == 'transformed!', 'Transformed output must be as expected' # TODO: Add in files test a clear example of using disablePermissions, and link to it from the wiki # TODO: test normal project linking, static and dynamic: get_library should not need to be told what to link! # TODO: deprecate llvm optimizations, dlmalloc, etc. in emscripten.py. + def test_cmake(self): + # On Windows, we want to build cmake-generated Makefiles with mingw32-make instead of e.g. cygwin make, since mingw32-make + # understands Windows paths, and cygwin make additionally produces a cryptic 'not valid bitcode file' errors on files that + # *are* valid bitcode files. + + if os.name == 'nt': + make_command = 'mingw32-make' + emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten.cmake') + else: + make_command = 'make' + emscriptencmaketoolchain = path_from_root('cmake', 'Platform', 'Emscripten_unix.cmake') + + cmake_cases = ['target_js', 'target_html'] + cmake_outputs = ['hello_world.js', 'hello_world_gles.html'] + for i in range(0, 2): + 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([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]: + # Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't try to produce an output file. + process = Popen([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), 'this_file_is_missing.c', '-o', 'this_output_file_should_never_exist.js'], stdout=PIPE, stderr=PIPE) + process.communicate() + assert process.returncode is not 0, 'Trying to compile a nonexisting file should return with a nonzero error code!' + assert os.path.exists('this_output_file_should_never_exist.js') == False, 'Emcc should not produce an output file when build fails!' + def test_Os(self): for opt in ['s', '0']: - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world.c'), '-O' + opt], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-O' + opt], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert ('emcc: warning: -Os is ignored (use -O0, -O1, -O2)' in output[1]) == (opt == 's'), 'warn on -Os when necessary' assert os.path.exists('a.out.js'), '\n'.join(output) @@ -7473,10 +8023,10 @@ class Test { int main() { printf("hello, world!\n"); - return 1; + return 0; } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fcatch-undefined-behavior']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-fcatch-undefined-behavior']).communicate() self.assertContained('hello, world!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_unaligned_memory(self): @@ -7496,9 +8046,25 @@ def test_unaligned_memory(self): printf("data[1,2] 16bit: %x\n", *(Bit16u*)(data+1)); } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() self.assertContained('data: 67452301\ndata[0,1] 16bit: 2301\ndata[1,2] 16bit: 4523', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_unaligned_memory_2(self): + open(os.path.join(self.get_dir(), 'test.cpp'), 'w').write(r''' + #include + #include + + int main( int argc, char ** argv ) + { + std::string testString( "Hello, World!" ); + + printf( "testString = %s\n", testString.c_str() ); + return 0; + } + ''') + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'test.cpp'), '-s', 'UNALIGNED_MEMORY=1']).communicate() + self.assertContained('testString = Hello, World!', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_l_link(self): # Linking with -lLIBNAME and -L/DIRNAME should work @@ -7522,12 +8088,97 @@ def test_l_link(self): } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c']).communicate() shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so')) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile']).communicate() self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs' + def test_multiply_defined_libsymbols(self): + lib = "int mult() { return 1; }" + lib_name = os.path.join(self.get_dir(), 'libA.c') + open(lib_name, 'w').write(lib) + a2 = "void x() {}" + a2_name = os.path.join(self.get_dir(), 'a2.c') + open(a2_name, 'w').write(a2) + b2 = "void y() {}" + b2_name = os.path.join(self.get_dir(), 'b2.c') + open(b2_name, 'w').write(b2) + main = r''' + #include + int mult(); + int main() { + printf("result: %d\n", mult()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(lib_name, output_filename='libA.so') + + Building.emcc(a2_name, ['-L.', '-lA']) + Building.emcc(b2_name, ['-L.', '-lA']) + + Building.emcc(main_name, ['-L.', '-lA', a2_name+'.o', b2_name+'.o'], output_filename='a.out.js') + + self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_multiply_defined_libsymbols_2(self): + a = "int x() { return 55; }" + a_name = os.path.join(self.get_dir(), 'a.c') + open(a_name, 'w').write(a) + b = "int y() { return 2; }" + b_name = os.path.join(self.get_dir(), 'b.c') + open(b_name, 'w').write(b) + c = "int z() { return 5; }" + c_name = os.path.join(self.get_dir(), 'c.c') + open(c_name, 'w').write(c) + main = r''' + #include + int x(); + int y(); + int z(); + int main() { + printf("result: %d\n", x() + y() + z()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(a_name) # a.c.o + Building.emcc(b_name) # b.c.o + Building.emcc(c_name) # c.c.o + lib_name = os.path.join(self.get_dir(), 'libLIB.a') + Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b + + # a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though + Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js') + + self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_redundant_link(self): + lib = "int mult() { return 1; }" + lib_name = os.path.join(self.get_dir(), 'libA.c') + open(lib_name, 'w').write(lib) + main = r''' + #include + int mult(); + int main() { + printf("result: %d\n", mult()); + return 0; + } + ''' + main_name = os.path.join(self.get_dir(), 'main.c') + open(main_name, 'w').write(main) + + Building.emcc(lib_name, output_filename='libA.so') + + Building.emcc(main_name, ['libA.so']*2, output_filename='a.out.js') + + self.assertContained('result: 1', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_abspaths(self): # Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones. @@ -7538,7 +8189,7 @@ def test_abspaths(self): (['-Isubdir/something'], False), (['-Lsubdir/something'], False), ([], False)]: - err = Popen(['python', EMCC, 'main.c'] + args, stderr=PIPE).communicate()[1] + err = Popen([PYTHON, EMCC, 'main.c'] + args, stderr=PIPE).communicate()[1] assert ('emcc: warning: -I or -L of an absolute path encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' in err) == expected, err def test_local_link(self): @@ -7566,8 +8217,8 @@ def test_local_link(self): } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'libfile.cpp'), '-o', 'libfile.so']).communicate() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.'], stderr=PIPE).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'libfile.cpp'), '-o', 'libfile.so']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'subdir', 'libfile.so'), '-L.'], stderr=PIPE).communicate() self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_runtimelink_multi(self): @@ -7628,11 +8279,11 @@ class TestB { } ''') - Popen(['python', EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate() - Popen(['python', EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate() - Popen(['python', EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-I.']).communicate() + Popen([PYTHON, EMCC, 'testa.cpp', '-o', 'liba.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate() + Popen([PYTHON, EMCC, 'testb.cpp', '-o', 'libb.js', '-s', 'BUILD_AS_SHARED_LIB=2', '-s', 'LINKABLE=1', '-I.']).communicate() + Popen([PYTHON, EMCC, 'main.cpp', '-o', 'main.js', '-s', 'RUNTIME_LINKED_LIBS=["liba.js", "libb.js"]', '-I.']).communicate() - Popen(['python', EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate() + Popen([PYTHON, EMCC, 'main.cpp', 'testa.cpp', 'testb.cpp', '-o', 'full.js', '-I.']).communicate() self.assertContained('TestA\nTestB\nTestA\n', run_js('main.js', engine=SPIDERMONKEY_ENGINE)) @@ -7664,7 +8315,7 @@ def test_js_libraries(self): }); ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'), + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--js-library', os.path.join(self.get_dir(), 'mylib1.js'), '--js-library', os.path.join(self.get_dir(), 'mylib2.js')]).communicate() self.assertContained('hello from lib!\n*32*\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -7691,40 +8342,16 @@ def test_identical_basenames(self): void printey() { printf("hello there\\n"); } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), os.path.join(self.get_dir(), 'bar', 'main.cpp')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), os.path.join(self.get_dir(), 'bar', 'main.cpp')]).communicate() self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) # ditto with first creating .o files try_delete(os.path.join(self.get_dir(), 'a.out.js')) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'foo', 'main.o')]).communicate() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'bar', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'foo', 'main.o')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'bar', 'main.cpp'), '-o', os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - def test_remove_duplicates(self): - # can happen with .a files. we do a best-effort, removing dupes - open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' - #include - void printey() { printf("bye bye\\n"); } - int main() { - printey(); - return 0; - } - ''') - open(os.path.join(self.get_dir(), 'side.cpp'), 'w').write(''' - #include - void printey() { printf("bye bye\\n"); } - ''') - - # without --remove-duplicates, we fail - err = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'side.cpp')], stderr=PIPE).communicate()[1] - assert not os.path.exists('a.out.js') - assert 'multiply' in err - - # with it, we succeed - err = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'side.cpp'), '--remove-duplicates'], stderr=PIPE).communicate()[1] - self.assertContained('bye bye', run_js(os.path.join(self.get_dir(), 'a.out.js'))) - def test_main_a(self): # if main() is in a .a, we need to pull in that .a @@ -7744,12 +8371,12 @@ def test_main_a(self): int f() { return 12346; } ''') - Popen(['python', EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate() - Popen(['python', EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate() + Popen([PYTHON, EMCC, main_name, '-c', '-o', main_name+'.bc']).communicate() + Popen([PYTHON, EMCC, other_name, '-c', '-o', other_name+'.bc']).communicate() - Popen(['python', EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate() + Popen([PYTHON, EMAR, 'cr', main_name+'.a', main_name+'.bc']).communicate() - Popen(['python', EMCC, other_name+'.bc', main_name+'.a']).communicate() + Popen([PYTHON, EMCC, other_name+'.bc', main_name+'.a']).communicate() self.assertContained('result: 12346.', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -7760,8 +8387,8 @@ def test_dup_o_in_a(self): printf("a\n"); } ''') - Popen(['python', EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() - Popen(['python', EMAR, 'rc', 'liba.a', 'common.o']).communicate() + Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() + Popen([PYTHON, EMAR, 'rc', 'liba.a', 'common.o']).communicate() open('common.c', 'w').write(r''' #include @@ -7769,8 +8396,8 @@ def test_dup_o_in_a(self): printf("b\n"); } ''') - Popen(['python', EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() - Popen(['python', EMAR, 'rc', 'libb.a', 'common.o']).communicate() + Popen([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o']).communicate() + Popen([PYTHON, EMAR, 'rc', 'libb.a', 'common.o']).communicate() open('main.c', 'w').write(r''' void a(void); @@ -7780,7 +8407,7 @@ def test_dup_o_in_a(self): b(); } ''') - Popen(['python', EMCC, 'main.c', '-L.', '-la', '-lb']).communicate() + Popen([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb']).communicate() self.assertContained('a\nb\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -7799,11 +8426,11 @@ def test_embed_file(self): } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) # preload twice, should not err - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_embed_file_dup(self): @@ -7835,7 +8462,7 @@ def test_embed_file_dup(self): } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'tst']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'tst']).communicate() self.assertContained('|frist|\n|sacond|\n|thard|\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_multidynamic_link(self): @@ -7880,7 +8507,7 @@ def test_multidynamic_link(self): ''') # This lets us link the same dynamic lib twice. We will need to link it in manually at the end. - compiler = ['python', EMCC, '--ignore-dynamic-linking'] + compiler = [PYTHON, EMCC, '--ignore-dynamic-linking'] # Build libfile normally into an .so Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate() @@ -7890,7 +8517,7 @@ def test_multidynamic_link(self): Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate() # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -7910,7 +8537,7 @@ def test_js_link(self): Module.print(MESSAGE); ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_sdl_endianness(self): @@ -7923,7 +8550,7 @@ def test_sdl_endianness(self): return 0; } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() self.assertContained('1234, 1234, 4321\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_warn_undefined(self): @@ -7939,12 +8566,39 @@ def test_warn_undefined(self): return 0; } ''') - output = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'WARN_ON_UNDEFINED_SYMBOLS=1'], stderr=PIPE).communicate() - self.assertContained('Unresolved symbol: _something\n', output[1]) + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'WARN_ON_UNDEFINED_SYMBOLS=1'], stderr=PIPE).communicate() + self.assertContained('Unresolved symbol: _something', output[1]) - output = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate() self.assertNotContained('Unresolved symbol: _something\n', output[1]) + def test_toobig(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include + + #define BYTES 100*1024*1024 + + int main(int argc, char **argv) { + if (argc == 100) { + static char buf[BYTES]; + static char buf2[BYTES]; + for (int i = 0; i < BYTES; i++) { + buf[i] = i*i; + buf2[i] = i/3; + } + for (int i = 0; i < BYTES; i++) { + buf[i] = buf2[i/2]; + buf2[i] = buf[i/3]; + } + printf("%d\n", buf[10] + buf2[20]); + } + return 0; + } + ''') + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')], stderr=PIPE).communicate()[1] + assert 'Emscripten failed' in output, output + assert 'warning: very large fixed-size structural type' in output, output + def test_prepost(self): open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' #include @@ -7960,7 +8614,7 @@ def test_prepost(self): }; ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) # never run, so no preRun or postRun @@ -7970,7 +8624,7 @@ def test_prepost(self): # noInitialRun prevents run for no_initial_run in [0, 1]: - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read() open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) assert ('hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js'))) != no_initial_run, 'only run if no noInitialRun' @@ -7989,7 +8643,7 @@ def test_prepost(self): preInit: function() { Module.print('pre-init') } }; ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepost2(self): @@ -8008,7 +8662,7 @@ def test_prepost2(self): open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' Module.postRun = function() { Module.print('post-run') }; ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepre(self): @@ -8027,13 +8681,13 @@ def test_prepre(self): open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' Module.preRun.push(function() { Module.print('prepre') }); ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() self.assertContained('prepre\npre-run\nhello from main\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_fix_closure(self): input = path_from_root('tests', 'test-fix-closure.js') expected = path_from_root('tests', 'test-fix-closure.out.js') - Popen(['python', path_from_root('tools', 'fix_closure.py'), input, 'out.js']).communicate(input) + Popen([PYTHON, path_from_root('tools', 'fix_closure.py'), input, 'out.js']).communicate(input) output = open('out.js').read() assert '0,zzz_Q_39fa,0' in output assert 'function(a,c)' not in output # should be uninlined, so it gets a name @@ -8048,7 +8702,7 @@ def test_js_optimizer(self): (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(), ['simplifyExpressionsPre', 'optimizeShiftsAggressive']), # Make sure that optimizeShifts handles functions with shift statements. - (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3.js')).read(), + (path_from_root('tools', 'test-js-optimizer-t3.js'), open(path_from_root('tools', 'test-js-optimizer-t3-output.js')).read(), ['optimizeShiftsAggressive']), (path_from_root('tools', 'test-js-optimizer-regs.js'), open(path_from_root('tools', 'test-js-optimizer-regs-output.js')).read(), ['registerize']), @@ -8063,10 +8717,21 @@ def test_js_optimizer(self): def test_m_mm(self): open(os.path.join(self.get_dir(), 'foo.c'), 'w').write('''#include ''') for opt in ['M', 'MM']: - output, err = Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE, stderr=PIPE).communicate() + output, err = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'foo.c'), '-' + opt], stdout=PIPE, stderr=PIPE).communicate() assert 'foo.o: ' in output, '-%s failed to produce the right output: %s' % (opt, output) assert 'error' not in err, 'Unexpected stderr: ' + err + def test_chunking(self): + if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') + if multiprocessing.cpu_count() < 2: return self.skip('need multiple cores') + try: + os.environ['EMCC_DEBUG'] = '1' + output, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_libcxx.cpp'), '-O1'], stdout=PIPE, stderr=PIPE).communicate() + assert 'phase 2 working on 3 chunks' in err, err + assert 'splitting up js optimization into 2 chunks' in err, err + finally: + del os.environ['EMCC_DEBUG'] + def test_scons(self): # also incidentally tests c++11 integration in llvm 3.1 try_delete(os.path.join(self.get_dir(), 'test')) shutil.copytree(path_from_root('tests', 'scons'), os.path.join(self.get_dir(), 'test')) @@ -8084,8 +8749,9 @@ def test_embind(self): # XXX TODO (['--bind', '-O2'], False) ]: print args, fail + self.clear() try_delete(self.in_dir('a.out.js')) - Popen(['python', EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate() assert os.path.exists(self.in_dir('a.out.js')) == (not fail) if not fail: output = run_js(self.in_dir('a.out.js')) @@ -8149,8 +8815,8 @@ def test_llvm_nativizer(self): open(os.path.join(self.get_dir(), 'somefile.binary'), 'w').write('''waka waka############################''') open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''') open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate() - Popen(['python', path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')]).communicate(input)[0] + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate() + Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')]).communicate(input)[0] output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate() self.assertIdentical('''size: 37 data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35 @@ -8168,7 +8834,7 @@ def test_llvm_nativizer(self): self.assertIdentical('texte\n', output[1]) def test_emconfig(self): - output = Popen(['python', EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() try: assert output == LLVM_ROOT except: @@ -8176,16 +8842,16 @@ def test_emconfig(self): raise invalid = 'Usage: em-config VAR_NAME' # Don't accept variables that do not exist - output = Popen(['python', EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + output = Popen([PYTHON, EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() assert output == invalid # Don't accept no arguments - output = Popen(['python', EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + output = Popen([PYTHON, EMCONFIG], stdout=PIPE, stderr=PIPE).communicate()[0].strip() assert output == invalid # Don't accept more than one variable - output = Popen(['python', EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + output = Popen([PYTHON, EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() assert output == invalid # Don't accept arbitrary python code - output = Popen(['python', EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() + output = Popen([PYTHON, EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE).communicate()[0].strip() assert output == invalid def test_link_s(self): @@ -8209,10 +8875,10 @@ def test_link_s(self): } } ''') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'main.o']).communicate() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'supp.cpp'), '-o', 'supp.o']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'main.o']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'supp.cpp'), '-o', 'supp.o']).communicate() - output = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.o'), '-s', os.path.join(self.get_dir(), 'supp.o'), '-s', 'SAFE_HEAP=1'], stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o'), '-s', os.path.join(self.get_dir(), 'supp.o'), '-s', 'SAFE_HEAP=1'], stderr=PIPE).communicate() self.assertContained('emcc: warning: treating -s as linker option', output[1]) output = run_js('a.out.js') assert 'yello' in output, 'code works' @@ -8226,7 +8892,7 @@ def test_conftest_s_flag_passing(self): } ''') os.environ["EMMAKEN_JUST_CONFIGURE"] = "1" - cmd = ['python', EMCC, '-s', 'ASSERTIONS=1', os.path.join(self.get_dir(), 'conftest.c'), '-o', 'conftest'] + cmd = [PYTHON, EMCC, '-s', 'ASSERTIONS=1', os.path.join(self.get_dir(), 'conftest.c'), '-o', 'conftest'] output = Popen(cmd, stderr=PIPE).communicate() del os.environ["EMMAKEN_JUST_CONFIGURE"] self.assertNotContained('emcc: warning: treating -s as linker option', output[1]) @@ -8236,21 +8902,35 @@ def test_crunch(self): # crunch should not be run if a .crn exists that is more recent than the .dds shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') time.sleep(0.1) - Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() assert os.stat('test.data').st_size < 0.25*os.stat('ship.dds').st_size, 'Compressed should be much smaller than dds' crunch_time = os.stat('ship.crn').st_mtime dds_time = os.stat('ship.dds').st_mtime assert crunch_time > dds_time, 'Crunch is more recent' # run again, should not recrunch! time.sleep(0.1) - Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() assert crunch_time == os.stat('ship.crn').st_mtime, 'Crunch is unchanged' # update dds, so should recrunch time.sleep(0.1) os.utime('ship.dds', None) - Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() assert crunch_time < os.stat('ship.crn').st_mtime, 'Crunch was changed' + def test_headless(self): + if SPIDERMONKEY_ENGINE not in JS_ENGINES: return self.skip('cannot run without spidermonkey due to node limitations (Uint8ClampedArray etc.)') + + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png')) + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_canvas.c'), '-s', 'HEADLESS=1']).communicate() + output = run_js('a.out.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE) + assert '''Init: 0 +Font: 0x1 +Sum: 0 +you should see two lines of text in different colors and a blue rectangle +SDL_Quit called (and ignored) +done. +''' in output, output + elif 'browser' in str(sys.argv): # Browser tests. @@ -8429,7 +9109,7 @@ def reftest(self, expected): def test_html(self): # test HTML generation. self.reftest(path_from_root('tests', 'htmltest.png')) - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.html', '--pre-js', 'reftest.js']).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.html', '--pre-js', 'reftest.js']).communicate() self.run_browser('something.html', 'You should see "hello, world!" and a colored cube.', '/report_result?0') def build_native_lzma(self): @@ -8446,7 +9126,7 @@ def build_native_lzma(self): def test_split(self): # test HTML generation. self.reftest(path_from_root('tests', 'htmltest.png')) - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '--split', '100', '--pre-js', 'reftest.js']).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '--split', '100', '--pre-js', 'reftest.js']).communicate() assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' assert os.path.exists(os.path.join(self.get_dir(), 'something_functions.js')), 'must be functions js file' assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' @@ -8537,7 +9217,7 @@ def test_split(self): def test_split_in_source_filenames(self): self.reftest(path_from_root('tests', 'htmltest.png')) - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '-g', '--split', '100', '--pre-js', 'reftest.js']).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_sdl.cpp'), '-o', 'something.js', '-g', '--split', '100', '--pre-js', 'reftest.js']).communicate() assert os.path.exists(os.path.join(self.get_dir(), 'something.js')), 'must be main js file' assert os.path.exists(self.get_dir() + '/something/' + path_from_root('tests', 'hello_world_sdl.cpp.js')), 'must be functions js file' assert os.path.exists(os.path.join(self.get_dir(), 'something.include.html')), 'must be js include file' @@ -8639,7 +9319,7 @@ def test_compression(self): ''')) self.build_native_lzma() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), 'LZMA.decompress')]).communicate() @@ -8672,13 +9352,13 @@ def make_main(path): ''' % path)) make_main('somefile.txt') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'somefile.txt', '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'somefile.txt', '-o', 'page.html']).communicate() self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') # By absolute path make_main(os.path.join(self.get_dir(), 'somefile.txt')) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', os.path.join(self.get_dir(), 'somefile.txt'), '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', os.path.join(self.get_dir(), 'somefile.txt'), '-o', 'page.html']).communicate() self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') # Should still work with -o subdir/.. @@ -8688,7 +9368,7 @@ def make_main(path): os.mkdir(os.path.join(self.get_dir(), 'dirrey')) except: pass - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', os.path.join(self.get_dir(), 'somefile.txt'), '-o', 'dirrey/page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', os.path.join(self.get_dir(), 'somefile.txt'), '-o', 'dirrey/page.html']).communicate() self.run_browser('dirrey/page.html', 'You should see |load me right before|.', '/report_result?1') # With FS.preloadFile @@ -8699,7 +9379,7 @@ def make_main(path): }; ''') make_main('someotherfile.txt') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-o', 'page.html']).communicate() self.run_browser('page.html', 'You should see |load me right before|.', '/report_result?1') def test_multifile(self): @@ -8736,12 +9416,12 @@ def test_multifile(self): ''')) # by individual files - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr/data1.txt', '--preload-file', 'subdirr/moar/data2.txt', '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr/data1.txt', '--preload-file', 'subdirr/moar/data2.txt', '-o', 'page.html']).communicate() self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') os.remove('page.html') # by directory, and remove files to make sure - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr', '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--preload-file', 'subdirr', '-o', 'page.html']).communicate() shutil.rmtree(os.path.join(self.get_dir(), 'subdirr')) self.run_browser('page.html', 'You should see two cool numbers', '/report_result?1') @@ -8772,7 +9452,7 @@ def test_compressed_file(self): ''')) self.build_native_lzma() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt', '--preload-file', 'datafile2.txt', + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-o', 'page.html', '--preload-file', 'datafile.txt', '--preload-file', 'datafile2.txt', '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), 'LZMA.decompress')]).communicate() @@ -8787,7 +9467,7 @@ def test_sdl_image(self): shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?600') def test_sdl_image_compressed(self): @@ -8801,7 +9481,7 @@ def test_sdl_image_compressed(self): open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read()).replace('screenshot.jpg', basename)) self.build_native_lzma() - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '--preload-file', basename, '-o', 'page.html', + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '--preload-file', basename, '-o', 'page.html', '--compression', '%s,%s,%s' % (path_from_root('third_party', 'lzma.js', 'lzma-native'), path_from_root('third_party', 'lzma.js', 'lzma-decoder.js'), 'LZMA.decompress')]).communicate() @@ -8821,7 +9501,7 @@ def test_sdl_image_prepare_data(self): def test_sdl_canvas(self): open(os.path.join(self.get_dir(), 'sdl_canvas.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas.c'), '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_sdl_key(self): @@ -8849,7 +9529,7 @@ def test_sdl_key(self): ''') open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?510510') def test_sdl_mouse(self): @@ -8882,7 +9562,7 @@ def test_sdl_mouse(self): ''') open(os.path.join(self.get_dir(), 'sdl_mouse.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_mouse.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_mouse.c'), '-O2', '--minify', '0', '-o', 'page.html', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '', '/report_result?740') def test_sdl_audio(self): @@ -8892,81 +9572,81 @@ def test_sdl_audio(self): open(os.path.join(self.get_dir(), 'sdl_audio.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio.c')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes - Popen(['python', EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '--preload-file', 'bad.ogg', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_sdl_audio_quickload(self): open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read())) # use closure to check for a possible bug with closure minifying away newer Audio() attributes - Popen(['python', EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() + Popen([PYTHON, EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() self.run_browser('page.html', '', '/report_result?1') def test_sdl_gl_read(self): # SDL, OpenGL, readPixels open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_gl_read.c'), '-o', 'something.html']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_gl_read.c'), '-o', 'something.html']).communicate() self.run_browser('something.html', '.', '/report_result?1') def test_sdl_ogl(self): # SDL, OpenGL, textures, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-gray-purple.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_ogl.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_sdl_ogl_defaultmatrixmode(self): # SDL, OpenGL, textures, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-gray-purple.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_sdl_ogl_p(self): # Immediate mode with pointers shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-gray.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_p.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_ogl_p.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_sdl_fog_simple(self): # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-fog-simple.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_simple.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_fog_simple.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') def test_sdl_fog_negative(self): # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-fog-negative.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_negative.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_fog_negative.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') def test_sdl_fog_density(self): # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-fog-density.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_density.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_fog_density.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') def test_sdl_fog_exp2(self): # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-fog-exp2.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_exp2.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_fog_exp2.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') def test_sdl_fog_linear(self): # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-fog-linear.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_linear.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'sdl_fog_linear.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') def test_worker(self): # Test running in a web worker - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('worker.js'), output self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone @@ -9049,7 +9729,7 @@ def test_chunked_synchronous_xhr(self): prejs_file.close() # vs. os.path.join(self.get_dir(), filename) # vs. path_from_root('tests', 'hello_world_gles.c') - Popen(['python', EMCC, path_from_root('tests', c_source_filename), '-g', '-s', 'SMALL_CHUNKS=1', '-o', worker_filename, + Popen([PYTHON, EMCC, path_from_root('tests', c_source_filename), '-g', '-s', 'SMALL_CHUNKS=1', '-o', worker_filename, '--pre-js', prejs_filename]).communicate() chunkSize = 1024 @@ -9100,19 +9780,19 @@ def do_GET(s): def test_glgears(self): self.reftest(path_from_root('tests', 'gears.png')) - Popen(['python', EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', '--pre-js', 'reftest.js']).communicate() self.run_browser('something.html', 'You should see animating gears.', '/report_result?0') def test_glgears_animation(self): - Popen(['python', EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')]).communicate() self.run_browser('something.html', 'You should see animating gears.', '/report_gl_result?true') def test_glgears_bad(self): # Make sure that OpenGL ES is not available if typed arrays are not used - Popen(['python', EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', '-s', 'USE_TYPED_ARRAYS=0', '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')]).communicate() @@ -9120,7 +9800,7 @@ def test_glgears_bad(self): def test_glgears_deriv(self): self.reftest(path_from_root('tests', 'gears.png')) - Popen(['python', EMCC, path_from_root('tests', 'hello_world_gles_deriv.c'), '-o', 'something.html', + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles_deriv.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', '--pre-js', 'reftest.js']).communicate() self.run_browser('something.html', 'You should see animating gears.', '/report_result?0') src = open('something.html').read() @@ -9151,7 +9831,7 @@ def book_path(*pathelems): args = ['--preload-file', 'smoke.tga', '-O2'] # test optimizations and closure here as well for more coverage self.reftest(book_path(basename.replace('.bc', '.png'))) - Popen(['python', EMCC, program, '-o', 'program.html', '--pre-js', 'reftest.js'] + args).communicate() + Popen([PYTHON, EMCC, program, '-o', 'program.html', '--pre-js', 'reftest.js'] + args).communicate() self.run_browser('program.html', '', '/report_result?0') def btest(self, filename, expected=None, reference=None, reference_slack=0, args=[]): # TODO: use in all other tests @@ -9167,7 +9847,7 @@ def btest(self, filename, expected=None, reference=None, reference_slack=0, args shutil.copyfile(path_from_root('tests', filename), os.path.join(self.get_dir(), filename)) self.reftest(path_from_root('tests', reference)) args = args + ['--pre-js', 'reftest.js'] - Popen(['python', EMCC, os.path.join(self.get_dir(), filename), '-o', 'test.html'] + args).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), filename), '-o', 'test.html'] + args).communicate() if type(expected) is str: expected = [expected] self.run_browser('test.html', '.', ['/report_result?' + e for e in expected]) @@ -9261,11 +9941,15 @@ def test_sdl_canvas_twice(self): def test_sdl_maprgba(self): self.btest('sdl_maprgba.c', reference='sdl_maprgba.png', reference_slack=3) + def test_sdl_rotozoom(self): + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'example.png')) + self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'example.png']) + def zzztest_sdl_canvas_palette_2(self): # XXX disabled until we have proper automation open(os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas_palette_2.c')).read())) open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('Module[\'preRun\'] = function() { SDL.defaults.copyOnLock = false }') - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate() self.run_browser('page.html', '') def test_s3tc(self): @@ -9276,7 +9960,7 @@ def test_s3tc_crunch(self): shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds', 'water.dds'], stdout=open('pre.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'test.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds', 'water.dds'], stdout=open('pre.js', 'w')).communicate() assert os.stat('test.data').st_size < 0.5*(os.stat('ship.dds').st_size+os.stat('bloom.dds').st_size+os.stat('water.dds').st_size), 'Compressed should be smaller than dds' shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed @@ -9287,8 +9971,8 @@ def test_s3tc_crunch_split(self): # load several datafiles/outputs of file packa shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') - Popen(['python', FILE_PACKAGER, 'asset_a.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds'], stdout=open('asset_a.js', 'w')).communicate() - Popen(['python', FILE_PACKAGER, 'asset_b.data', '--pre-run', '--crunch', '--preload', 'water.dds'], stdout=open('asset_b.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'asset_a.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds'], stdout=open('asset_a.js', 'w')).communicate() + Popen([PYTHON, FILE_PACKAGER, 'asset_b.data', '--pre-run', '--crunch', '--preload', 'water.dds'], stdout=open('asset_b.js', 'w')).communicate() shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed @@ -9304,11 +9988,14 @@ def test_tex_nonbyte(self): def test_float_tex(self): self.btest('float_tex.cpp', reference='float_tex.png') + def test_subdata(self): + self.btest('gl_subdata.cpp', reference='float_tex.png') + def test_runtimelink(self): main, supp = self.setup_runtimelink_test() open(self.in_dir('supp.cpp'), 'w').write(supp) - Popen(['python', EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '--closure', '0']).communicate() + Popen([PYTHON, EMCC, self.in_dir('supp.cpp'), '-o', 'supp.js', '-s', 'LINKABLE=1', '-s', 'BUILD_AS_SHARED_LIB=2', '-O2', '--closure', '0']).communicate() shutil.move(self.in_dir('supp.js'), self.in_dir('supp.so')) self.btest(main, args=['-s', 'LINKABLE=1', '-s', 'RUNTIME_LINKED_LIBS=["supp.so"]', '-DBROWSER=1', '-O2', '--closure', '0'], expected='76') @@ -9328,11 +10015,11 @@ def test_pre_run_deps(self): self.btest('pre_run_deps.cpp', expected='10', args=['--pre-js', 'pre.js']) def test_worker_api(self): - Popen(['python', EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-s', 'EXPORTED_FUNCTIONS=["_one"]']).communicate() self.btest('worker_api_main.cpp', expected='566') def test_worker_api_2(self): - Popen(['python', EMCC, path_from_root('tests', 'worker_api_2_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-O2', '--minify', '0', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two", "_three", "_four"]']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'worker_api_2_worker.cpp'), '-o', 'worker.js', '-s', 'BUILD_AS_WORKER=1', '-O2', '--minify', '0', '-s', 'EXPORTED_FUNCTIONS=["_one", "_two", "_three", "_four"]']).communicate() self.btest('worker_api_2_main.cpp', args=['-O2', '--minify', '0'], expected='11') pids_to_clean = [] @@ -9424,7 +10111,7 @@ def test_zz_websockets(self): def make_relay_server(self, port1, port2): def relay_server(q): print >> sys.stderr, 'creating relay server on ports %d,%d' % (port1, port2) - proc = Popen(['python', path_from_root('tests', 'socket_relay.py'), str(port1), str(port2)]) + proc = Popen([PYTHON, path_from_root('tests', 'socket_relay.py'), str(port1), str(port2)]) q.put(proc.pid) proc.communicate() return relay_server @@ -9433,7 +10120,7 @@ def test_zz_websockets_bi(self): try: with self.WebsockHarness(8992, self.make_relay_server(8992, 8994)): with self.WebsockHarness(8994, no_server=True): - Popen(['python', EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=8995']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=8995']).communicate() self.btest('websockets_bi.c', expected='2499') finally: self.clean_pids() @@ -9442,7 +10129,7 @@ def test_zz_websockets_bi_listen(self): try: with self.WebsockHarness(6992, self.make_relay_server(6992, 6994)): with self.WebsockHarness(6994, no_server=True): - Popen(['python', EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side.c'), '-o', 'side.html', '-DSOCKK=6995']).communicate() self.btest('websockets_bi_listener.c', expected='2499') finally: self.clean_pids() @@ -9458,7 +10145,7 @@ def test_zz_websockets_bi_bigdata(self): try: with self.WebsockHarness(3992, self.make_relay_server(3992, 3994)): with self.WebsockHarness(3994, no_server=True): - Popen(['python', EMCC, path_from_root('tests', 'websockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'websockets_bi_side_bigdata.c'), '-o', 'side.html', '-DSOCKK=3995', '-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]).communicate() self.btest('websockets_bi_bigdata.c', expected='0', args=['-s', 'SOCKET_DEBUG=0', '-I' + path_from_root('tests')]) finally: self.clean_pids() @@ -9468,11 +10155,11 @@ def test_zz_enet(self): shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet')) pwd = os.getcwd() os.chdir(self.in_dir('enet')) - Popen(['python', path_from_root('emconfigure'), './configure']).communicate() - Popen(['python', path_from_root('emmake'), 'make']).communicate() + Popen([PYTHON, path_from_root('emconfigure'), './configure']).communicate() + Popen([PYTHON, path_from_root('emmake'), 'make']).communicate() enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')] os.chdir(pwd) - Popen(['python', EMCC, path_from_root('tests', 'enet_server.c'), '-o', 'server.html'] + enet).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'enet_server.c'), '-o', 'server.html'] + enet).communicate() try: with self.WebsockHarness(1234, self.make_relay_server(1234, 1236)): @@ -9575,7 +10262,7 @@ def do_benchmark(self, name, src, args=[], expected_output='FAIL', emcc_args=[]) final_filename = os.path.join(dirname, name + '.js') try_delete(final_filename) - output = Popen(['python', EMCC, filename, #'-O3', + output = Popen([PYTHON, EMCC, filename, #'-O3', '-O2', '-s', 'INLINING_LIMIT=0', '-s', 'DOUBLE_MODE=0', '-s', 'PRECISE_I64_MATH=0', '-s', 'TOTAL_MEMORY=100*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024', '-o', final_filename] + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() @@ -9632,7 +10319,7 @@ def test_primes(self): curri++; } printf("lastprime: %d.\\n", curri-1); - return 1; + return 0; } ''' self.do_benchmark('primes', src, [], 'lastprime: 1297001.') @@ -9655,7 +10342,7 @@ def test_memops(self): final = final % 1000; } printf("final: %d.\\n", final); - return 1; + return 0; } ''' self.do_benchmark('memops', src, [], 'final: 720.') @@ -9698,7 +10385,7 @@ def zzztest_files(self): unlink(buf); } printf("ok"); - return 1; + return 0; } ''' self.do_benchmark(src, [], 'ok') @@ -9741,7 +10428,7 @@ def test_copy(self): } } printf("sum:%d\n", total); - return 1; + return 0; } ''' self.do_benchmark('copy', src, [], 'sum:9928\n', emcc_args=['-s', 'QUANTUM_SIZE=4', '-s', 'USE_TYPED_ARRAYS=2']) @@ -9768,7 +10455,7 @@ def test_corrections(self): } } printf("final: %d:%d.\n", f, s); - return 1; + return 0; } ''' self.do_benchmark('corrections', src, [], 'final: 826:14324.', emcc_args=['-s', 'CORRECT_SIGNS=1', '-s', 'CORRECT_OVERFLOWS=1', '-s', 'CORRECT_ROUNDINGS=1']) @@ -9805,6 +10492,8 @@ def test_dlmalloc(self): assert os.path.exists(CONFIG_FILE), 'To run these tests, we need a (working!) %s file to already exist' % EM_CONFIG + assert not os.environ.get('EMCC_DEBUG'), 'do not run sanity checks in debug mode!' + shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup') def restore(): shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) @@ -9815,7 +10504,7 @@ def wipe(): try_delete(CONFIG_FILE) try_delete(SANITY_FILE) - commands = [[EMCC], ['python', path_from_root('tests', 'runner.py'), 'blahblah']] + commands = [[EMCC], [PYTHON, path_from_root('tests', 'runner.py'), 'blahblah']] def mtime(filename): return os.stat(filename).st_mtime @@ -9831,7 +10520,7 @@ def do(self, command): if type(command) is not list: command = [command] if command[0] == EMCC: - command = ['python'] + command + command = [PYTHON] + command return Popen(command, stdout=PIPE, stderr=STDOUT).communicate()[0] @@ -9881,7 +10570,7 @@ def test_firstrun(self): # The guessed config should be ok XXX This depends on your local system! it is possible `which` guesses wrong try_delete('a.out.js') - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('hello, world!', run_js('a.out.js'), output) # Second run, with bad EM_CONFIG @@ -10037,10 +10726,11 @@ def test_emcc(self): self.assertContained(SANITY_MESSAGE, output) # but with EMCC_DEBUG=1 we should check - assert not os.environ.get('EMCC_DEBUG'), 'do not run sanity checks in debug mode!' - os.environ['EMCC_DEBUG'] = '1' - output = self.check_working(EMCC) - del os.environ['EMCC_DEBUG'] + try: + os.environ['EMCC_DEBUG'] = '1' + output = self.check_working(EMCC) + finally: + del os.environ['EMCC_DEBUG'] self.assertContained(SANITY_MESSAGE, output) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) @@ -10077,11 +10767,13 @@ def test_emcc(self): return 0; } ''') - Popen(['python', EMCC, os.path.join(dirname, 'main.cpp'), '-o', os.path.join(dirname, 'a.out.js')]).communicate() + Popen([PYTHON, EMCC, os.path.join(dirname, 'main.cpp'), '-o', os.path.join(dirname, 'a.out.js')]).communicate() del os.environ['EM_CONFIG'] self.assertContained('hello from emcc with no config file', run_js(os.path.join(dirname, 'a.out.js'))) shutil.rmtree(dirname) + try_delete(CANONICAL_TEMP_DIR) + def test_emcc_caching(self): INCLUDING_MESSAGE = 'emcc: including X' BUILDING_MESSAGE = 'emcc: building X for cache' @@ -10095,8 +10787,8 @@ def test_emcc_caching(self): assert not os.path.exists(EMCC_CACHE) try: - emcc_debug = os.environ.get('EMCC_DEBUG') os.environ['EMCC_DEBUG'] ='1' + self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') # Building a file that doesn't need cached stuff should not trigger cache generation output = self.do([EMCC, path_from_root('tests', 'hello_world.cpp')]) @@ -10112,8 +10804,6 @@ def test_emcc_caching(self): ll_name1 = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-2-ll.ll') ll_name2 = os.path.join(TEMP_DIR, 'emscripten_temp', 'emcc-3-ll.ll') - self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') - # Building a file that *does* need dlmalloc *should* trigger cache generation, but only the first time for filename, libname in [('hello_malloc.cpp', 'dlmalloc'), ('hello_libcxx.cpp', 'libcxx')]: for i in range(3): @@ -10137,17 +10827,16 @@ def test_emcc_caching(self): assert os.path.exists(os.path.join(EMCC_CACHE, libname + '.bc')) if libname == 'libcxx': print os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size, os.stat(basebc_name).st_size, os.stat(dcebc_name).st_size - assert os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size > 2000000, 'libc++ is big' - assert os.stat(basebc_name).st_size > 2000000, 'libc++ is indeed big' - assert os.stat(dcebc_name).st_size < 1500000, 'Dead code elimination must remove most of libc++' + assert os.stat(os.path.join(EMCC_CACHE, libname + '.bc')).st_size > 1800000, 'libc++ is big' + assert os.stat(basebc_name).st_size > 1800000, 'libc++ is indeed big' + assert os.stat(dcebc_name).st_size < 750000, 'Dead code elimination must remove most of libc++' # should only have metadata in -O0, not 1 and 2 ll = open(ll_name).read() if (ll.count('\n!') < 10) == (i == 0): # a few lines are left even in -O1 and -O2 print i, 'll metadata should be removed in -O1 and O2 by default', ll[-300:] assert False finally: - if emcc_debug: - os.environ['EMCC_DEBUG'] = emcc_debug + del os.environ['EMCC_DEBUG'] # Manual cache clearing assert os.path.exists(EMCC_CACHE) @@ -10155,6 +10844,8 @@ def test_emcc_caching(self): assert ERASING_MESSAGE in output assert not os.path.exists(EMCC_CACHE) + try_delete(CANONICAL_TEMP_DIR) + def test_relooper(self): restore() for phase in range(2): # 0: we wipe the relooper dir. 1: we have it, so should just update @@ -10162,16 +10853,92 @@ def test_relooper(self): try_delete(RELOOPER) for i in range(4): - print phase, i + print >> sys.stderr, phase, i opt = min(i, 2) try_delete('a.out.js') - output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '--closure', '0'], + output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop.cpp'), '-O' + str(opt), '--closure', '0'], stdout=PIPE, stderr=PIPE).communicate() self.assertContained('hello, world!', run_js('a.out.js')) output = '\n'.join(output) assert ('bootstrapping relooper succeeded' in output) == (i == 2), 'only bootstrap on first O2: ' + output assert os.path.exists(RELOOPER) == (i >= 2), 'have relooper on O2: ' + output - assert ('L2 : do {' in open('a.out.js').read()) == (i >= 2), 'reloop code on O2: ' + output + src = open('a.out.js').read() + main = src.split('function _main() {')[1].split('\n}\n')[0] + assert ('while (1) {' in main) == (i >= 2), 'reloop code on O2: ' + src + assert ('switch' not in main) == (i >= 2), 'reloop code on O2: ' + src + + def test_jcache(self): + PRE_LOAD_MSG = 'loading pre from jcache' + PRE_SAVE_MSG = 'saving pre to jcache' + FUNC_CHUNKS_LOAD_MSG = ' funcchunks from jcache' + FUNC_CHUNKS_SAVE_MSG = ' funcchunks to jcache' + JSFUNC_CHUNKS_LOAD_MSG = 'jsfuncchunks from jcache' + JSFUNC_CHUNKS_SAVE_MSG = 'jsfuncchunks to jcache' + + restore() + Cache.erase() + + try: + os.environ['EMCC_DEBUG'] = '1' + self.working_dir = os.path.join(TEMP_DIR, 'emscripten_temp') + if not os.path.exists(self.working_dir): os.makedirs(self.working_dir) + + assert not os.path.exists(JCache.get_cachename('emscript_files')) + + srcs = {} + used_jcache = False + + for args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected in [ + ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_world_loop.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), + ([], 'hello_world_loop.cpp', False, False, False, False, False, False, []), + # new + ([], 'hello_world.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_world.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_world.cpp', False, True, False, True, False, True, []), + ([], 'hello_world.cpp', False, False, False, False, False, False, []), + # go back to old file, experience caching + (['--jcache'], 'hello_world_loop.cpp', False, True, False, True, False, True, []), + # new, large file + ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), + (['--jcache'], 'hello_malloc.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_malloc.cpp', False, True, False, True, False, True, []), + ([], 'hello_malloc.cpp', False, False, False, False, False, False, []), + # new, huge file + ([], 'hello_libcxx.cpp', False, False, False, False, False, False, ('2 chunks', '3 chunks')), + (['--jcache'], 'hello_libcxx.cpp', True, False, True, False, True, False, []), + (['--jcache'], 'hello_libcxx.cpp', False, True, False, True, False, True, []), + ([], 'hello_libcxx.cpp', False, False, False, False, False, False, []), + # finally, build a file close to the previous, to see that some chunks are found in the cache and some not + (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, True, True, True, True, []), # win on pre, mix on funcs, mix on jsfuncs + (['--jcache'], 'hello_libcxx_mod1.cpp', False, True, False, True, False, True, []), + ]: + print >> sys.stderr, args, input_file, expect_pre_save, expect_pre_load, expect_funcs_save, expect_funcs_load, expect_jsfuncs_save, expect_jsfuncs_load, expected + self.clear() + out, err = Popen([PYTHON, EMCC, '-O2', '--closure', '0', path_from_root('tests', input_file)] + args, stdout=PIPE, stderr=PIPE).communicate() + errtail = err.split('emcc invocation')[-1] + self.assertContained('hello, world!', run_js('a.out.js'), errtail) + assert (PRE_SAVE_MSG in err) == expect_pre_save, errtail + assert (PRE_LOAD_MSG in err) == expect_pre_load, errtail + assert (FUNC_CHUNKS_SAVE_MSG in err) == expect_funcs_save, errtail + assert (FUNC_CHUNKS_LOAD_MSG in err) == expect_funcs_load, errtail + assert (JSFUNC_CHUNKS_SAVE_MSG in err) == expect_jsfuncs_save, errtail + assert (JSFUNC_CHUNKS_LOAD_MSG in err) == expect_jsfuncs_load, errtail + for expect in expected: assert expect in err, expect + ' ? ' + errtail + curr = open('a.out.js').read() + if input_file not in srcs: + srcs[input_file] = curr + else: + open('/home/alon/Dev/emscripten/a', 'w').write(srcs[input_file]) + open('/home/alon/Dev/emscripten/b', 'w').write(curr) + assert abs(len(curr)/float(len(srcs[input_file]))-1)<0.01, 'contents may shift in order, but must remain the same size %d vs %d' % (len(curr), len(srcs[input_file])) + '\n' + errtail + used_jcache = used_jcache or ('--jcache' in args) + assert used_jcache == os.path.exists(JCache.get_cachename('emscript_files')) + #print >> sys.stderr, errtail + + finally: + del os.environ['EMCC_DEBUG'] else: raise Exception('Test runner is confused: ' + str(sys.argv)) diff --git a/tests/sdl_canvas.c b/tests/sdl_canvas.c index 5d1c849c2b146..7dcfa0432194e 100644 --- a/tests/sdl_canvas.c +++ b/tests/sdl_canvas.c @@ -53,6 +53,8 @@ int main(int argc, char **argv) { SDL_Quit(); + printf("done.\n"); + int result = sum > 3000 && sum < 5000; // varies a little on different browsers, font differences? REPORT_RESULT(); diff --git a/tests/sdl_ogl_p.c b/tests/sdl_ogl_p.c index 949aaa44ad27a..fcc53a4090412 100644 --- a/tests/sdl_ogl_p.c +++ b/tests/sdl_ogl_p.c @@ -145,9 +145,9 @@ int main(int argc, char *argv[]) // Render the last item using oldschool glBegin etc glBegin( GL_TRIANGLE_STRIP ); glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); - glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); - glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); - glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glTexCoord2i( 1, 0 ); glVertex2f( 300, 300 ); + glTexCoord2i( 1, 1 ); { float vals[3] = { 300, 400, 0 }; glVertex3fv(vals); } + glTexCoord2i( 0, 1 ); { float vals[2] = { 500, 410 }; glVertex2fv(vals); } glEnd(); SDL_GL_SwapBuffers(); diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c new file mode 100644 index 0000000000000..cb6295cc167ef --- /dev/null +++ b/tests/sdl_rotozoom.c @@ -0,0 +1,48 @@ +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_rotozoom.h" + +#ifdef EMSCRIPTEN +#include "emscripten.h" +#endif + +SDL_Surface *screen; +SDL_Surface *sprite[4]; + +void mainloop() { + int i; + SDL_Rect rect = { 0, 0, 100, 100 }; + for (i = 0; i < 4; i++) { + rect.x = i & 1 ? 200 : 0; + rect.y = i & 2 ? 200 : 0; + SDL_BlitSurface(sprite[i], 0, screen, &rect); + SDL_UpdateRect(screen, 0, 0, 0, 0); + } +} + +int main(int argc, char **argv) { + SDL_Init(SDL_INIT_VIDEO); + + screen = SDL_SetVideoMode(400, 400, 32, SDL_SWSURFACE); + + sprite[0] = IMG_Load("example.png"); + sprite[1] = SDL_CreateRGBSurface(SDL_SWSURFACE, 100, 100, 32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF); + SDL_FillRect(sprite[1], 0, 0xA0A0A0A0); + sprite[2] = zoomSurface(sprite[0], 0.5, 0.5, SMOOTHING_ON); + sprite[3] = zoomSurface(sprite[1], 0.5, 0.5, SMOOTHING_ON); + + mainloop(); + +#ifndef EMSCRIPTEN + SDL_Event evt; + while (1) { + if (SDL_PollEvent(&evt) != 0 && evt.type == SDL_QUIT) break; + //mainloop(); + SDL_Delay(33); + } +#endif + + SDL_Quit(); + + return 1; +} diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png new file mode 100644 index 0000000000000..83ec058931781 Binary files /dev/null and b/tests/sdl_rotozoom.png differ diff --git a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py index a16d32be9721c..c9aacc56227fb 100644 --- a/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py +++ b/third_party/CppHeaderParser/CppHeaderParser/CppHeaderParser.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 # # Author: Jashua R. Cloutier (contact via sourceforge username:senexcanis) # diff --git a/third_party/CppHeaderParser/setup.py b/third_party/CppHeaderParser/setup.py index 3c254a038ebd3..f81ff431bedf4 100644 --- a/third_party/CppHeaderParser/setup.py +++ b/third_party/CppHeaderParser/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -*- coding: utf-8 -*- import sys, glob diff --git a/third_party/closure-compiler/README b/third_party/closure-compiler/README index 77ab89db02635..270cfc278ca5a 100644 --- a/third_party/closure-compiler/README +++ b/third_party/closure-compiler/README @@ -171,7 +171,7 @@ lib/args4j.jar Args4j URL: https://args4j.dev.java.net/ -Version: 2.0.12 +Version: 2.0.16 License: MIT Description: @@ -187,7 +187,7 @@ lib/guava.jar Guava Libraries URL: http://code.google.com/p/guava-libraries/ -Version: r08 +Version: 13.0.1 License: Apache License 2.0 Description: Google's core Java libraries. @@ -230,7 +230,7 @@ lib/junit.jar JUnit URL: http://sourceforge.net/projects/junit/ -Version: 4.8.2 +Version: 4.10 License: Common Public License 1.0 Description: A framework for writing and running automated tests in Java. @@ -244,7 +244,7 @@ lib/protobuf-java.jar Protocol Buffers URL: http://code.google.com/p/protobuf/ -Version: 2.3.0 +Version: 2.4.1 License: New BSD License Description: Supporting libraries for protocol buffers, @@ -281,9 +281,9 @@ Local Modifications: None --- Code in: -tools/maven-ant-tasks-2.1.1.jar +tools/maven-ant-tasks-2.1.3.jar URL: http://maven.apache.org -Version 2.1.1 +Version 2.1.3 License: Apache License 2.0 Description: Maven Ant tasks are used to manage dependencies and to install/deploy to diff --git a/third_party/closure-compiler/compiler.jar b/third_party/closure-compiler/compiler.jar index da191b019d3b8..e23352ff0bdbf 100644 Binary files a/third_party/closure-compiler/compiler.jar and b/third_party/closure-compiler/compiler.jar differ diff --git a/third_party/demangler.py b/third_party/demangler.py index 1066643be576d..66e8eaea68195 100644 --- a/third_party/demangler.py +++ b/third_party/demangler.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 ''' Simple tool to run the demangler. diff --git a/third_party/websockify/run b/third_party/websockify/run index 54db9b8dc4a92..2802254fcbe7b 100755 --- a/third_party/websockify/run +++ b/third_party/websockify/run @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 import websockify diff --git a/third_party/websockify/websockify/websocket.py b/third_party/websockify/websockify/websocket.py index 3f539a1d77eba..9ce13159787a3 100644 --- a/third_party/websockify/websockify/websocket.py +++ b/third_party/websockify/websockify/websocket.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' Python WebSocket library with support for "wss://" encryption. diff --git a/third_party/websockify/websockify/websocketproxy.py b/third_party/websockify/websockify/websocketproxy.py index 1154d9257ba7c..18d0a28447e89 100755 --- a/third_party/websockify/websockify/websocketproxy.py +++ b/third_party/websockify/websockify/websocketproxy.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' A WebSocket to TCP socket proxy with support for "wss://" encryption. diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index 0c7c814fd40ac..d2c165a2e012d 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' Use CppHeaderParser to parse some C++ headers, and generate binding code for them. diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js index 00647fca32b5d..01f092d5bd797 100644 --- a/tools/eliminator/eliminator-test-output.js +++ b/tools/eliminator/eliminator-test-output.js @@ -6132,5 +6132,12 @@ function _mallocNoU($bytes) { return $mem_0; return null; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU"] +function phi() { + if (wat()) { + var $10 = 1; + } else { + var $10 = (_init_mparams() | 0) != 0; + } + var $10; +} diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js index e44f28ad0f581..8b3763053247a 100644 --- a/tools/eliminator/eliminator-test.js +++ b/tools/eliminator/eliminator-test.js @@ -8828,5 +8828,15 @@ function _mallocNoU($bytes) { return $mem_0; return null; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU"] +function phi() { + if (wat()) { + var $10 = 1; + } else { + var $7=_init_mparams(); + var $8=(($7)|0)!=0; + var $10 = $8; + } + var $10; +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a", "b", "c", "f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1", "_inflate", "_malloc", "_mallocNoU", "asm", "phi"] diff --git a/tools/eliminator/safe-eliminator-test-output.js b/tools/eliminator/safe-eliminator-test-output.js index bb3f17e67ade4..57f0a74390f1e 100644 --- a/tools/eliminator/safe-eliminator-test-output.js +++ b/tools/eliminator/safe-eliminator-test-output.js @@ -82,5 +82,4 @@ function a($directory) { print(zzz1); } while (1); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["a"] diff --git a/tools/emconfiguren.py b/tools/emconfiguren.py index fd7b7549be168..35d83b87c3280 100755 --- a/tools/emconfiguren.py +++ b/tools/emconfiguren.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import sys print >> sys.stderr, '\n\nemconfiguren.py is deprecated! use "emconfigure"\n\n' diff --git a/tools/emmaken.py b/tools/emmaken.py index ae4794f0f3623..77405761764ee 100755 --- a/tools/emmaken.py +++ b/tools/emmaken.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import sys print >> sys.stderr, '\n\nemmaken.py is deprecated! use "emcc"\n\n' diff --git a/tools/emmakenxx.py b/tools/emmakenxx.py index 1c31f3c27afc5..0af3f99e11814 100755 --- a/tools/emmakenxx.py +++ b/tools/emmakenxx.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' see emmaken.py @@ -14,5 +14,5 @@ def path_from_root(*pathelems): emmaken = path_from_root('tools', 'emmaken.py') os.environ['EMMAKEN_CXX'] = '1' -exit(subprocess.call(['python', emmaken] + sys.argv[1:])) +exit(subprocess.call([PYTHON, emmaken] + sys.argv[1:])) diff --git a/tools/exec_llvm.py b/tools/exec_llvm.py index 71c0f18d6cc27..2685f9e440523 100755 --- a/tools/exec_llvm.py +++ b/tools/exec_llvm.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 ''' Small utility to execute some llvm bitcode. diff --git a/tools/fix_closure.py b/tools/fix_closure.py index ab2dacbcc3310..91d2a86674e26 100755 --- a/tools/fix_closure.py +++ b/tools/fix_closure.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' With very very large projects, closure compiler can translate FUNCTION_TABLE into something like diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index a9a181b1f68f9..6216173823e81 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -214,6 +214,7 @@ function traverse(node, pre, post, stack) { // Only walk through the generated functions function traverseGenerated(ast, pre, post, stack) { + assert(generatedFunctions); traverse(ast, function(node) { if (node[0] == 'defun' && isGenerated(node[1])) { traverse(node, pre, post, stack); @@ -223,6 +224,7 @@ function traverseGenerated(ast, pre, post, stack) { } function traverseGeneratedFunctions(ast, callback) { + assert(generatedFunctions); traverse(ast, function(node) { if (node[0] == 'defun' && isGenerated(node[1])) { callback(node); @@ -1462,7 +1464,7 @@ function eliminate(ast, memSafe) { for (var name in locals) { if (definitions[name] == 1 && uses[name] == 1) { potentials[name] = 1; - } else if (uses[name] == 0) { + } else if (uses[name] == 0 && (!definitions[name] || definitions[name] <= 1)) { // no uses, no def or 1 def (cannot operate on phis, and the llvm optimizer will remove unneeded phis anyhow) var hasSideEffects = false; if (values[name]) { traverse(values[name], function(node, type) { @@ -1914,6 +1916,5 @@ do { js = js.replace(/\n *\n/g, '\n'); } while (js != old); print(js); -if (metadata && printMetadata) print(metadata); print('\n'); diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py index 8681280aee5ab..5bed4cb7a090c 100644 --- a/tools/js_optimizer.py +++ b/tools/js_optimizer.py @@ -1,5 +1,8 @@ -import os, sys, subprocess, multiprocessing +import os, sys, subprocess, multiprocessing, re +import shared + +temp_files = shared.TempFiles() __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def path_from_root(*pathelems): @@ -17,13 +20,15 @@ def run_on_chunk(command): filename = command[2] # XXX hackish output = subprocess.Popen(command, stdout=subprocess.PIPE).communicate()[0] assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output - filename += '.jo.js' + filename = temp_files.get(os.path.basename(filename) + '.jo.js').name f = open(filename, 'w') f.write(output) f.close() return filename -def run(filename, passes, js_engine): +def run(filename, passes, js_engine, jcache): + if jcache: shared.JCache.ensure() + if type(passes) == str: passes = [passes] @@ -37,64 +42,129 @@ def run(filename, passes, js_engine): suffix = '' if suffix_start >= 0: suffix = js[suffix_start:js.find('\n', suffix_start)] + '\n' + # if there is metadata, we will run only on the generated functions. If there isn't, we will run on everything. + generated = set(eval(suffix[len(suffix_marker)+1:])) + + if not suffix and jcache: + # JCache cannot be used without metadata, since it might reorder stuff, and that's dangerous since only generated can be reordered + # This means jcache does not work after closure compiler runs, for example. But you won't get much benefit from jcache with closure + # anyhow (since closure is likely the longest part of the build). + if DEBUG: print >>sys.stderr, 'js optimizer: no metadata, so disabling jcache' + jcache = False + + # If we process only generated code, find that and save the rest on the side + func_sig = re.compile('function (_[\w$]+)\(') + if suffix: + pos = 0 + gen_start = 0 + gen_end = 0 + while 1: + m = func_sig.search(js, pos) + if not m: break + pos = m.end() + ident = m.group(1) + if ident in generated: + if not gen_start: + gen_start = m.start() + assert gen_start + gen_end = js.find('\n}\n', m.end()) + 3 + assert gen_end > gen_start + pre = js[:gen_start] + post = js[gen_end:] + js = js[gen_start:gen_end] + else: + pre = '' + post = '' # Pick where to split into chunks, so that (1) they do not oom in node/uglify, and (2) we can run them in parallel - chunks = [] - i = 0 - f_start = 0 - while True: - f_end = f_start - while f_end-f_start < BEST_JS_PROCESS_SIZE and f_end != -1: - f_end = js.find('\n}\n', f_end+1) - chunk = js[f_start:(-1 if f_end == -1 else f_end+3)] + suffix - temp_file = filename + '.p%d.js' % i - #if DEBUG: print >> sys.stderr, ' chunk %d: %d bytes' % (i, (f_end if f_end >= 0 else len(js)) - f_start) - i += 1 - f_start = f_end+3 - done = f_end == -1 or f_start >= len(js) - if done and len(chunks) == 0: break # do not write anything out, just use the input file - f = open(temp_file, 'w') - f.write(chunk) - f.close() - chunks.append(temp_file) - if done: break - - if len(chunks) == 0: - chunks.append(filename) - - # XXX Use '--nocrankshaft' to disable crankshaft to work around v8 bug 1895, needed for older v8/node (node 0.6.8+ should be ok) - commands = map(lambda chunk: [js_engine, JS_OPTIMIZER, chunk] + passes, chunks) - - if len(chunks) > 1: - # We are splitting into chunks. Hopefully we can do that in parallel - commands = map(lambda command: command + ['noPrintMetadata'], commands) - filename += '.jo.js' - - fail = None - cores = min(multiprocessing.cpu_count(), chunks) - if cores < 2: - fail = 'python reports you have %d cores' % cores - #elif WINDOWS: - # fail = 'windows (see issue 663)' # This seems fixed with adding emcc.py that imports this file - - if not fail: + # If we have metadata, we split only the generated code, and save the pre and post on the side (and do not optimize them) + parts = map(lambda part: part, js.split('\n}\n')) + funcs = [] + for i in range(len(parts)): + func = parts[i] + if i < len(parts)-1: func += '\n}\n' # last part needs no } + m = func_sig.search(func) + if m: + ident = m.group(1) + else: + if suffix: continue # ignore whitespace + ident = 'anon_%d' % i + funcs.append((ident, func)) + parts = None + total_size = len(js) + js = None + + chunks = shared.JCache.chunkify(funcs, BEST_JS_PROCESS_SIZE, 'jsopt' if jcache else None) + + if jcache: + # load chunks from cache where we can # TODO: ignore small chunks + cached_outputs = [] + def load_from_cache(chunk): + keys = [chunk] + shortkey = shared.JCache.get_shortkey(keys) # TODO: share shortkeys with later code + out = shared.JCache.get(shortkey, keys) + if out: + cached_outputs.append(out) + return False + return True + chunks = filter(load_from_cache, chunks) + if len(cached_outputs) > 0: + if DEBUG: print >> sys.stderr, ' loading %d jsfuncchunks from jcache' % len(cached_outputs) + else: + cached_outputs = [] + + if len(chunks) > 0: + def write_chunk(chunk, i): + temp_file = temp_files.get('.jsfunc_%d.ll' % i).name + f = open(temp_file, 'w') + f.write(chunk) + f.write(suffix) + f.close() + return temp_file + filenames = [write_chunk(chunks[i], i) for i in range(len(chunks))] + else: + filenames = [] + + if len(filenames) > 0: + # XXX Use '--nocrankshaft' to disable crankshaft to work around v8 bug 1895, needed for older v8/node (node 0.6.8+ should be ok) + commands = map(lambda filename: [js_engine, JS_OPTIMIZER, filename, 'noPrintMetadata'] + passes, filenames) + + cores = min(multiprocessing.cpu_count(), filenames) + if len(chunks) > 1 and cores >= 2: # We can parallelize - if DEBUG: print >> sys.stderr, 'splitting up js optimization into %d chunks, using %d cores' % (len(chunks), cores) + if DEBUG: print >> sys.stderr, 'splitting up js optimization into %d chunks, using %d cores (total: %.2f MB)' % (len(chunks), cores, total_size/(1024*1024.)) pool = multiprocessing.Pool(processes=cores) filenames = pool.map(run_on_chunk, commands, chunksize=1) else: # We can't parallize, but still break into chunks to avoid uglify/node memory issues - if DEBUG: print >> sys.stderr, 'splitting up js optimization into %d chunks (not in parallel because %s)' % (len(chunks), fail) + if len(chunks) > 1 and DEBUG: print >> sys.stderr, 'splitting up js optimization into %d chunks' % (len(chunks)) filenames = [run_on_chunk(command) for command in commands] + else: + filenames = [] - f = open(filename, 'w') - for out_file in filenames: - f.write(open(out_file).read()) - f.write(suffix) + filename += '.jo.js' + f = open(filename, 'w') + f.write(pre); + for out_file in filenames: + f.write(open(out_file).read()) f.write('\n') - f.close() - return filename - else: - # one simple chunk, just do it - return run_on_chunk(commands[0]) + if jcache: + for cached in cached_outputs: + f.write(cached); # TODO: preserve order + f.write('\n') + f.write(post); + # No need to write suffix: if there was one, it is inside post which exists when suffix is there + f.write('\n') + f.close() + + if jcache: + # save chunks to cache + for i in range(len(chunks)): + chunk = chunks[i] + keys = [chunk] + shortkey = shared.JCache.get_shortkey(keys) + shared.JCache.set(shortkey, keys, open(filenames[i]).read()) + if DEBUG and len(chunks) > 0: print >> sys.stderr, ' saving %d jsfuncchunks to jcache' % len(chunks) + + return filename diff --git a/tools/ll-strip.py b/tools/ll-strip.py index b03e4f3a85cb3..a08da47885d9c 100755 --- a/tools/ll-strip.py +++ b/tools/ll-strip.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 import sys, re diff --git a/tools/make_minigzip.py b/tools/make_minigzip.py index 60177318a9a99..0c96457b50adf 100644 --- a/tools/make_minigzip.py +++ b/tools/make_minigzip.py @@ -9,5 +9,5 @@ print 'Building minigzip' -Popen(['python', shared.EMCC, '-O2', shared.path_from_root('tests', 'zlib', 'minigzip.c'), zlib, '-o', shared.path_from_root('tools', 'minigzip.js')]).communicate() +Popen(['python2', shared.EMCC, '-O2', shared.path_from_root('tests', 'zlib', 'minigzip.c'), zlib, '-o', shared.path_from_root('tools', 'minigzip.js')]).communicate() diff --git a/tools/namespacer.py b/tools/namespacer.py index 810689ebd5003..81b8d2d16ec33 100644 --- a/tools/namespacer.py +++ b/tools/namespacer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python2 ''' Tool that generates namespace boilerplate. Given diff --git a/tools/nativize_llvm.py b/tools/nativize_llvm.py index de78dce2af26e..e76b9a4e6a57e 100755 --- a/tools/nativize_llvm.py +++ b/tools/nativize_llvm.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2 ''' Small utility to build some llvm bitcode into native code. Useful when lli (called diff --git a/tools/reproduceriter.py b/tools/reproduceriter.py index a1912396ea936..c820978bf4c80 100755 --- a/tools/reproduceriter.py +++ b/tools/reproduceriter.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 ''' Reproducer Rewriter @@ -149,7 +149,7 @@ print 'add boilerplate...' open(os.path.join(out_dir, first_js), 'w').write( - (open(os.path.join(os.path.dirname(__file__), 'reproduceriter_shell.js')).read() % ( + (open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src', 'headless.js')).read() % ( window_location, window_location.split('?')[-1], on_idle or 'null', dirs_to_drop ) if shell else '') + open(os.path.join(os.path.dirname(__file__), 'reproduceriter.js')).read() + diff --git a/tools/scons/site_scons/site_tools/emscripten/__init__.py b/tools/scons/site_scons/site_tools/emscripten/__init__.py index 8ae2288e30343..ebd2c21bd3909 100644 --- a/tools/scons/site_scons/site_tools/emscripten/__init__.py +++ b/tools/scons/site_scons/site_tools/emscripten/__init__.py @@ -1,3 +1,3 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 from emscripten import exists, generate diff --git a/tools/scons/site_scons/site_tools/emscripten/emscripten.py b/tools/scons/site_scons/site_tools/emscripten/emscripten.py index ce704bf87f864..5abd4fee1f4ec 100644 --- a/tools/scons/site_scons/site_tools/emscripten/emscripten.py +++ b/tools/scons/site_scons/site_tools/emscripten/emscripten.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import os diff --git a/tools/settings_template_readonly.py b/tools/settings_template_readonly.py index 93ea203657333..970a8f8cb19e7 100644 --- a/tools/settings_template_readonly.py +++ b/tools/settings_template_readonly.py @@ -7,6 +7,7 @@ # this helps projects using emscripten find it EMSCRIPTEN_ROOT = os.path.expanduser(os.getenv('EMSCRIPTEN') or '{{{ EMSCRIPTEN_ROOT }}}') LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '{{{ LLVM_ROOT }}}') +PYTHON = os.path.expanduser(os.getenv('PYTHON') or '{{{ PYTHON }}}') # See below for notes on which JS engine(s) you need NODE_JS = os.path.expanduser(os.getenv('NODE') or '{{{ NODE }}}') diff --git a/tools/shared.py b/tools/shared.py index b8242db30ef85..72629131d3b5a 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -1,8 +1,64 @@ -import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess +import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess, hashlib, cPickle from subprocess import Popen, PIPE, STDOUT from tempfile import mkstemp -import js_optimizer +# On Windows python suffers from a particularly nasty bug if python is spawning new processes while python itself is spawned from some other non-console process. +# Use a custom replacement for Popen on Windows to avoid the "WindowsError: [Error 6] The handle is invalid" errors when emcc is driven through cmake or mingw32-make. +# See http://bugs.python.org/issue3905 +class WindowsPopen: + def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, + shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + # (stdin, stdout, stderr) store what the caller originally wanted to be done with the streams. + # (stdin_, stdout_, stderr_) will store the fixed set of streams that workaround the bug. + self.stdin_ = stdin + self.stdout_ = stdout + self.stderr_ = stderr + + # If the caller wants one of these PIPEd, we must PIPE them all to avoid the 'handle is invalid' bug. + if self.stdin_ == PIPE or self.stdout_ == PIPE or self.stderr_ == PIPE: + if self.stdin_ == None: + self.stdin_ = PIPE + if self.stdout_ == None: + self.stdout_ = PIPE + if self.stderr_ == None: + self.stderr_ = PIPE + + # Call the process with fixed streams. + self.process = subprocess.Popen(args, bufsize, executable, self.stdin_, self.stdout_, self.stderr_, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) + + def communicate(self, input=None): + output = self.process.communicate(input) + self.returncode = self.process.returncode + + # If caller never wanted to PIPE stdout or stderr, route the output back to screen to avoid swallowing output. + if self.stdout == None and self.stdout_ == PIPE and len(output[0].strip()) > 0: + print >> sys.stdout, output[0] + if self.stderr == None and self.stderr_ == PIPE and len(output[1].strip()) > 0: + print >> sys.stderr, output[1] + + # Return a mock object to the caller. This works as long as all emscripten code immediately .communicate()s the result, and doesn't + # leave the process object around for longer/more exotic uses. + if self.stdout == None and self.stderr == None: + return (None, None) + if self.stdout == None: + return (None, output[1]) + if self.stderr == None: + return (output[0], None) + return (output[0], output[1]) + + def poll(self): + return self.process.poll() + + def kill(self): + return self.process.kill() + +# Install our replacement Popen handler if we are running on Windows to avoid python spawn process function. +if os.name == 'nt': + Popen = WindowsPopen __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def path_from_root(*pathelems): @@ -36,10 +92,16 @@ def path_from_root(*pathelems): node = 'node' try: node = Popen(['which', 'node'], stdout=PIPE).communicate()[0].replace('\n', '') or \ - Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') + Popen(['which', 'nodejs'], stdout=PIPE).communicate()[0].replace('\n', '') or node except: pass config_file = config_file.replace('{{{ NODE }}}', node) + python = 'python' + try: + python = Popen(['which', 'python'], stdout=PIPE).communicate()[0].replace('\n', '') + except: + pass + config_file = config_file.replace('{{{ PYTHON }}}', python) # write open(CONFIG_FILE, 'w').write(config_file) @@ -54,6 +116,7 @@ def path_from_root(*pathelems): It contains our best guesses for the important paths, which are: LLVM_ROOT = %s + PYTHON = %s NODE_JS = %s EMSCRIPTEN_ROOT = %s @@ -61,7 +124,7 @@ def path_from_root(*pathelems): This command will now exit. When you are done editing those paths, re-run it. ============================================================================== -''' % (EM_CONFIG, CONFIG_FILE, llvm_root, node, __rootpath__) +''' % (EM_CONFIG, CONFIG_FILE, llvm_root, python, node, __rootpath__) sys.exit(0) try: config_text = open(CONFIG_FILE, 'r').read() if CONFIG_FILE else EM_CONFIG @@ -72,7 +135,7 @@ def path_from_root(*pathelems): # Expectations -EXPECTED_LLVM_VERSION = (3,1) +EXPECTED_LLVM_VERSION = (3,2) def check_clang_version(): expected = 'clang version ' + '.'.join(map(str, EXPECTED_LLVM_VERSION)) @@ -109,7 +172,7 @@ def check_node_version(): # we re-check sanity when the settings are changed) # We also re-check sanity and clear the cache when the version changes -EMSCRIPTEN_VERSION = '1.0.1a' +EMSCRIPTEN_VERSION = '1.1.0' def check_sanity(force=False): try: @@ -262,6 +325,12 @@ def clean_temp(): except: CLOSURE_COMPILER = path_from_root('third_party', 'closure-compiler', 'compiler.jar') +try: + PYTHON +except: + print >> sys.stderr, 'PYTHON not defined in ~/.emscripten, using "python"' + PYTHON = 'python' + try: JAVA except: @@ -277,7 +346,7 @@ def clean_temp(): # 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__x86_64__', '-U__i386', '-U__x86_64', '-U__SSE__', '-U__SSE2__', '-U__MMX__', '-UX87_DOUBLE_ROUNDING', '-UHAVE_GCC_ASM_FOR_X87', '-DEMSCRIPTEN', '-U__STRICT_ANSI__', '-U__CYGWIN__', - '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN'] + '-D__STDC__', '-Xclang', '-triple=i386-pc-linux-gnu', '-D__IEEE_LITTLE_ENDIAN', '-fno-math-errno'] USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') @@ -352,8 +421,8 @@ def get(self, suffix): return named_file def clean(self): - if DEBUG: - print >> sys.stderr, 'not cleaning up temp files since in debug mode, see them in %s' % EMSCRIPTEN_TEMP_DIR + if os.environ.get('EMCC_DEBUG_SAVE'): + print >> sys.stderr, 'not cleaning up temp files since in debug-save mode, see them in %s' % EMSCRIPTEN_TEMP_DIR return for filename in self.to_clean: try_delete(filename) @@ -442,6 +511,14 @@ def read_pgo_data(filename): 'overflows_lines': overflows_lines } +def unique_ordered(values): # return a list of unique values in an input list, without changing order (list(set(.)) would change order randomly) + seen = set() + def check(value): + if value in seen: return False + seen.add(value) + return True + return filter(check, values) + # Settings. A global singleton. Not pretty, but nicer than passing |, settings| everywhere class Settings: @@ -651,7 +728,12 @@ def open_make_err(i, mode='r'): return generated_libs @staticmethod - def link(files, target, remove_duplicates=False): + def remove_symbol(filename, symbol): + Popen([LLVM_EXTRACT, filename, '-delete', '-glob=' + symbol, '-o', filename], stderr=PIPE).communicate() + Popen([LLVM_EXTRACT, filename, '-delete', '-func=' + symbol, '-o', filename], stderr=PIPE).communicate() + + @staticmethod + def link(files, target): actual_files = [] unresolved_symbols = set(['main']) # tracking unresolveds is necessary for .a linking, see below. (and main is always a necessary symbol) resolved_symbols = set() @@ -675,6 +757,7 @@ def link(files, target, remove_duplicates=False): os.makedirs(temp_dir) os.chdir(temp_dir) contents = filter(lambda x: len(x) > 0, Popen([LLVM_AR, 't', f], stdout=PIPE).communicate()[0].split('\n')) + #print >> sys.stderr, ' considering archive', f, ':', contents if len(contents) == 0: print >> sys.stderr, 'Warning: Archive %s appears to be empty (recommendation: link an .so instead of .a)' % f else: @@ -685,44 +768,37 @@ def link(files, target, remove_duplicates=False): Popen([LLVM_AR, 'x', f], stdout=PIPE).communicate() # if absolute paths, files will appear there. otherwise, in this directory contents = map(lambda content: os.path.join(temp_dir, content), contents) contents = filter(os.path.exists, map(os.path.abspath, contents)) - needed = False # We add or do not add the entire archive. We let llvm dead code eliminate parts we do not need, instead of - # doing intra-dependencies between archive contents - for content in contents: - new_symbols = Building.llvm_nm(content) - # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld) - if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1: - needed = True - if needed: + added_contents = set() + added = True + #print >> sys.stderr, ' initial undef are now ', unresolved_symbols, '\n' + while added: # recursively traverse until we have everything we need + #print >> sys.stderr, ' running loop of archive including for', f + added = False for content in contents: - if Building.is_bitcode(content): - new_symbols = Building.llvm_nm(content) - resolved_symbols = resolved_symbols.union(new_symbols.defs) - unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs) - actual_files.append(content) + if content in added_contents: continue + new_symbols = Building.llvm_nm(content) + # Link in the .o if it provides symbols, *or* this is a singleton archive (which is apparently an exception in gcc ld) + #print >> sys.stderr, 'need', content, '?', unresolved_symbols, 'and we can supply', new_symbols.defs + #print >> sys.stderr, content, 'DEF', new_symbols.defs, '\n' + if new_symbols.defs.intersection(unresolved_symbols) or len(files) == 1: + if Building.is_bitcode(content): + #print >> sys.stderr, ' adding object', content, '\n' + resolved_symbols = resolved_symbols.union(new_symbols.defs) + unresolved_symbols = unresolved_symbols.union(new_symbols.undefs.difference(resolved_symbols)).difference(new_symbols.defs) + #print >> sys.stderr, ' undef are now ', unresolved_symbols, '\n' + actual_files.append(content) + added_contents.add(content) + added = True + #print >> sys.stderr, ' done running loop of archive including for', f finally: os.chdir(cwd) try_delete(target) - if remove_duplicates: - # Remove duplicate symbols. This is a workaround for how we compile .a files, we try to - # emulate ld behavior which is permissive TODO: cache llvm-nm results - seen_symbols = set() - print >> sys.stderr, actual_files - for actual in actual_files: - symbols = Building.llvm_nm(actual) - dupes = seen_symbols.intersection(symbols.defs) - if len(dupes) > 0: - print >> sys.stderr, 'emcc: warning: removing duplicates in', actual - for dupe in dupes: - print >> sys.stderr, 'emcc: warning: removing duplicate', dupe - Popen([LLVM_EXTRACT, actual, '-delete', '-glob=' + dupe, '-o', actual], stderr=PIPE).communicate() - Popen([LLVM_EXTRACT, actual, '-delete', '-func=' + dupe, '-o', actual], stderr=PIPE).communicate() - Popen([LLVM_EXTRACT, actual, '-delete', '-glob=.str', '-o', actual], stderr=PIPE).communicate() # garbage that appears here - seen_symbols = seen_symbols.union(symbols.defs) - # Finish link + actual_files = unique_ordered(actual_files) # tolerate people trying to link a.so a.so etc. + if DEBUG: print >>sys.stderr, 'emcc: llvm-linking:', actual_files output = Popen([LLVM_LINK] + actual_files + ['-o', target], stdout=PIPE).communicate()[0] - assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output + '\nemcc: If you get duplicate symbol errors, try --remove-duplicates' + assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output for temp_dir in temp_dirs: try_delete(temp_dir) @@ -807,13 +883,13 @@ def emcc(filename, args=[], output_filename=None, stdout=None, stderr=None, env= if output_filename is None: output_filename = filename + '.o' try_delete(output_filename) - Popen(ENV_PREFIX + ['python', EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate() + Popen([PYTHON, EMCC, filename] + args + ['-o', output_filename], stdout=stdout, stderr=stderr, env=env).communicate() assert os.path.exists(output_filename), 'emcc could not create output file' @staticmethod def emar(action, output_filename, filenames, stdout=None, stderr=None, env=None): try_delete(output_filename) - Popen(ENV_PREFIX + ['python', EMAR, action, output_filename] + filenames, stdout=stdout, stderr=stderr, env=env).communicate() + Popen([PYTHON, EMAR, action, output_filename] + filenames, stdout=stdout, stderr=stderr, env=env).communicate() if 'c' in action: assert os.path.exists(output_filename), 'emar could not create output file' @@ -824,7 +900,7 @@ def emscripten(filename, append_ext=True, extra_args=[]): # Run Emscripten settings = Settings.serialize() - compiler_output = timeout_run(Popen(ENV_PREFIX + ['python', EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling') + compiler_output = timeout_run(Popen([PYTHON, EMSCRIPTEN, filename + ('.o.ll' if append_ext else ''), '-o', filename + '.o.js'] + settings + extra_args, stdout=PIPE), None, 'Compiling') #print compiler_output # Detect compilation crashes and errors @@ -845,6 +921,12 @@ def can_use_unsafe_opts(): def can_inline(): return Settings.INLINING_LIMIT == 0 + @staticmethod + def get_safe_internalize(): + exports = ','.join(map(lambda exp: exp[1:], Settings.EXPORTED_FUNCTIONS)) + # internalize carefully, llvm 3.2 will remove even main if not told not to + return ['-internalize', '-internalize-public-api-list=' + exports] + @staticmethod def pick_llvm_opts(optimization_level): ''' @@ -880,7 +962,7 @@ def pick_llvm_opts(optimization_level): opts.append('-basicaa') # makes fannkuch slow but primes fast if Building.can_build_standalone(): - opts.append('-internalize') + opts += Building.get_safe_internalize() opts.append('-globalopt') opts.append('-ipsccp') @@ -948,8 +1030,8 @@ def pick_llvm_opts(optimization_level): return opts @staticmethod - def js_optimizer(filename, passes): - return js_optimizer.run(filename, passes, NODE_JS) + def js_optimizer(filename, passes, jcache): + return js_optimizer.run(filename, passes, NODE_JS, jcache) @staticmethod def closure_compiler(filename): @@ -988,7 +1070,8 @@ def is_ar(filename): b[6] == '>' and ord(b[7]) == 10 Building._is_ar_cache[filename] = sigcheck return sigcheck - except: + except Exception, e: + print >> sys.stderr, 'shared.Building.is_ar failed to test whether file \'%s\' is a llvm archive file! Failed on exception: %s' % (filename, e) return False @staticmethod @@ -1006,7 +1089,9 @@ def is_bitcode(filename): test_ll = os.path.join(EMSCRIPTEN_TEMP_DIR, 'test.ll') Building.llvm_dis(filename, test_ll) assert os.path.exists(test_ll) - except: + try_delete(test_ll) + except Exception, e: + print >> sys.stderr, 'shared.Building.is_bitcode failed to test whether file \'%s\' is a llvm bitcode file! Failed on exception: %s' % (filename, e) return False # look for magic signature @@ -1027,16 +1112,22 @@ def is_bitcode(filename): @staticmethod def ensure_relooper(): if os.path.exists(RELOOPER): return + curr = os.getcwd() try: ok = False print >> sys.stderr, '=======================================' print >> sys.stderr, 'bootstrapping relooper...' Cache.ensure() - RELOOPER_DIR = path_from_root('src', 'relooper') + os.chdir(path_from_root('src')) def make(opt_level): raw = RELOOPER + '.raw.js' - Building.emcc(os.path.join(RELOOPER_DIR, 'Relooper.cpp'), ['-I' + os.path.join(RELOOPER_DIR), '--post-js', os.path.join(RELOOPER_DIR, 'emscripten', 'glue.js'), '-s', 'TOTAL_MEMORY=52428800', '-s', 'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=["memcpy", "memset", "malloc", "free", "puts"]', '-O' + str(opt_level), '--closure', '0'], raw) + Building.emcc(os.path.join('relooper', 'Relooper.cpp'), ['-I' + os.path.join('relooper'), '--post-js', + os.path.join('relooper', 'emscripten', 'glue.js'), + '-s', 'TOTAL_MEMORY=52428800', + '-s', 'EXPORTED_FUNCTIONS=["_rl_set_output_buffer","_rl_make_output_buffer","_rl_new_block","_rl_delete_block","_rl_block_add_branch_to","_rl_new_relooper","_rl_delete_relooper","_rl_relooper_add_block","_rl_relooper_calculate","_rl_relooper_render"]', + '-s', 'DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=["memcpy", "memset", "malloc", "free", "puts"]', + '-O' + str(opt_level), '--closure', '0'], raw) f = open(RELOOPER, 'w') f.write("// Relooper, (C) 2012 Alon Zakai, MIT license, https://github.com/kripken/Relooper\n") f.write("var Relooper = (function() {\n"); @@ -1055,6 +1146,7 @@ def make(opt_level): print >> sys.stderr, '=======================================' ok = True finally: + os.chdir(curr) if not ok: print >> sys.stderr, 'bootstrapping relooper failed. You may need to manually create src/relooper.js by compiling it, see src/relooper/emscripten' 1/0 @@ -1090,6 +1182,152 @@ def get(shortname, creator): shutil.copyfile(creator(), cachename) return cachename +# JS-specific cache. We cache the results of compilation and optimization, +# so that in incremental builds we can just load from cache. +# We cache reasonably-large-sized chunks +class JCache: + dirname = os.path.join(Cache.dirname, 'jcache') + + @staticmethod + def ensure(): + Cache.ensure() + if not os.path.exists(JCache.dirname): + os.makedirs(JCache.dirname) + + @staticmethod + def get_shortkey(keys): + if type(keys) not in [list, tuple]: + keys = [keys] + ret = '' + for key in keys: + assert type(key) == str + ret += hashlib.md5(key).hexdigest() + return ret + + @staticmethod + def get_cachename(shortkey): + return os.path.join(JCache.dirname, shortkey) + + # Returns a cached value, if it exists. Make sure the full key matches + @staticmethod + def get(shortkey, keys): + #if DEBUG: print >> sys.stderr, 'jcache get?', shortkey + cachename = JCache.get_cachename(shortkey) + if not os.path.exists(cachename): + #if DEBUG: print >> sys.stderr, 'jcache none at all' + return + data = cPickle.Unpickler(open(cachename, 'rb')).load() + if len(data) != 2: + #if DEBUG: print >> sys.stderr, 'jcache error in get' + return + oldkeys = data[0] + if len(oldkeys) != len(keys): + #if DEBUG: print >> sys.stderr, 'jcache collision (a)' + return + for i in range(len(oldkeys)): + if oldkeys[i] != keys[i]: + #if DEBUG: print >> sys.stderr, 'jcache collision (b)' + return + #if DEBUG: print >> sys.stderr, 'jcache win' + return data[1] + + # Sets the cached value for a key (from get_key) + @staticmethod + def set(shortkey, keys, value): + cachename = JCache.get_cachename(shortkey) + cPickle.Pickler(open(cachename, 'wb')).dump([keys, value]) + #if DEBUG: + # for i in range(len(keys)): + # open(cachename + '.key' + str(i), 'w').write(keys[i]) + # open(cachename + '.value', 'w').write(value) + + # Given a set of functions of form (ident, text), and a preferred chunk size, + # generates a set of chunks for parallel processing and caching. + # It is very important to generate similar chunks in incremental builds, in + # order to maximize the chance of cache hits. To achieve that, we save the + # chunking used in the previous compilation of this phase, and we try to + # generate the same chunks, barring big differences in function sizes that + # violate our chunk size guideline. If caching is not used, chunking_file + # should be None + @staticmethod + def chunkify(funcs, chunk_size, chunking_file): + previous_mapping = None + if chunking_file: + chunking_file = JCache.get_cachename(chunking_file) + if os.path.exists(chunking_file): + try: + previous_mapping = cPickle.Unpickler(open(chunking_file, 'rb')).load() # maps a function identifier to the chunk number it will be in + except: + pass + chunks = [] + if previous_mapping: + # initialize with previous chunking + news = [] + for func in funcs: + ident, data = func + if not ident in previous_mapping: + news.append(func) + else: + n = previous_mapping[ident] + while n >= len(chunks): chunks.append([]) + chunks[n].append(func) + # add news and adjust for new sizes + spilled = news + for chunk in chunks: + size = sum([len(func[1]) for func in chunk]) + while size > 1.5*chunk_size and len(chunk) > 0: + spill = chunk.pop() + spilled.append(spill) + size -= len(spill[1]) + for chunk in chunks: + size = sum([len(func[1]) for func in chunk]) + while size < 0.66*chunk_size and len(spilled) > 0: + spill = spilled.pop() + chunk.append(spill) + size += len(spill[1]) + chunks = filter(lambda chunk: len(chunk) > 0, chunks) # might have empty ones, eliminate them + funcs = spilled # we will allocate these into chunks as if they were normal inputs + # initialize reasonably, the rest of the funcs we need to split out + curr = [] + total_size = 0 + for i in range(len(funcs)): + func = funcs[i] + curr_size = len(func[1]) + if total_size + curr_size < chunk_size: + curr.append(func) + total_size += curr_size + else: + chunks.append(curr) + curr = [func] + total_size = curr_size + if curr: + chunks.append(curr) + curr = None + if chunking_file: + # sort within each chunk, to keep the order identical + for chunk in chunks: + chunk.sort(key=lambda func: func[0]) + # save new mapping info + new_mapping = {} + for i in range(len(chunks)): + chunk = chunks[i] + for ident, data in chunk: + new_mapping[ident] = i + cPickle.Pickler(open(chunking_file, 'wb')).dump(new_mapping) + #if DEBUG: + # if previous_mapping: + # for ident in set(previous_mapping.keys() + new_mapping.keys()): + # if previous_mapping.get(ident) != new_mapping.get(ident): + # print >> sys.stderr, 'mapping inconsistency', ident, previous_mapping.get(ident), new_mapping.get(ident) + # for key, value in new_mapping.iteritems(): + # print >> sys.stderr, 'mapping:', key, value + return [''.join([func[1] for func in chunk]) for chunk in chunks] # remove function names + +class JS: + @staticmethod + def to_nice_ident(ident): # limited version of the JS function toNiceIdent + return ident.replace('%', '$').replace('@', '_'); + # Compression of code and data for smaller downloads class Compression: on = False @@ -1108,7 +1346,7 @@ def worth_it(original, compressed): def execute(cmd, *args, **kw): try: - return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) + return Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) except: if not isinstance(cmd, str): cmd = ' '.join(cmd) @@ -1128,3 +1366,5 @@ def unsuffixed(name): def unsuffixed_basename(name): return os.path.basename(unsuffixed(name)) +import js_optimizer + diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js index 9c9fa0634d405..730426b1eb65e 100644 --- a/tools/test-js-optimizer-output.js +++ b/tools/test-js-optimizer-output.js @@ -279,5 +279,4 @@ function notComps() { function tricky() { var $conv642 = $conv6374 - (($132 << 16 >> 16 | 0) / 2 & -1) & 65535; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps", "tricky"] diff --git a/tools/test-js-optimizer-regs-output.js b/tools/test-js-optimizer-regs-output.js index 2f91f52bd9bd6..90b67a4703c9a 100644 --- a/tools/test-js-optimizer-regs-output.js +++ b/tools/test-js-optimizer-regs-output.js @@ -225,5 +225,4 @@ function switchey(x) { r8 = x + 2; pp(r8); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["test", "primes", "atomic", "fcntl_open", "ex", "switchey"] diff --git a/tools/test-js-optimizer-t2-output.js b/tools/test-js-optimizer-t2-output.js index 62cd6e0d4ef08..0ae66be513e9e 100644 --- a/tools/test-js-optimizer-t2-output.js +++ b/tools/test-js-optimizer-t2-output.js @@ -88,5 +88,4 @@ function shifty($id2) { q(go() << 16); q(go() + 2 >> 2); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["shifty"] diff --git a/tools/test-js-optimizer-t2c-output.js b/tools/test-js-optimizer-t2c-output.js index f41bb81510ad4..726112ecc7890 100644 --- a/tools/test-js-optimizer-t2c-output.js +++ b/tools/test-js-optimizer-t2c-output.js @@ -14,5 +14,4 @@ function shifty() { q($13 + 13 << 2); q(h() >> 2 << 2); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["shifty"] diff --git a/tools/test-js-optimizer-t3-output.js b/tools/test-js-optimizer-t3-output.js new file mode 100644 index 0000000000000..924868fad1e2d --- /dev/null +++ b/tools/test-js-optimizer-t3-output.js @@ -0,0 +1,49 @@ +function _png_create_write_struct_2($user_png_ver, $error_ptr, $error_fn, $warn_fn, $mem_ptr, $malloc_fn, $free_fn) { + var $png_ptr$s2; + var label; + label = 2; + var setjmpTable = { + "2": (function(value) { + label = 5; + $call1 = value; + }), + dummy: 0 + }; + while (1) try { + switch (label) { + case 2: + var $png_ptr; + var $call = _png_create_struct(1); + $png_ptr = $call; + var $call1 = (HEAP32[$png_ptr >> 2] = label, 0); + label = 5; + break; + case 5: + var $2 = $png_ptr; + if (($call1 | 0) == 0) { + label = 4; + break; + } else { + label = 3; + break; + } + case 3: + var $4 = HEAP32[($png_ptr >> 2) + (148 >> 2)]; + _png_free($2, $4); + HEAP32[($png_ptr >> 2) + (148 >> 2)] = 0; + _png_destroy_struct($png_ptr); + var $retval_0 = 0; + label = 4; + break; + case 4: + var $retval_0; + return $retval_0; + default: + assert(0, "bad label: " + label); + } + } catch (e) { + if (!e.longjmp) throw e; + setjmpTable[e.label](e.value); + } +} +