aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xemcc17
-rwxr-xr-xemscripten.py179
-rw-r--r--src/analyzer.js2
-rw-r--r--src/compiler.js2
-rw-r--r--src/jsifier.js2
-rw-r--r--src/library.js45
-rw-r--r--src/library_sdl.js60
-rw-r--r--src/relooper/Relooper.cpp84
-rw-r--r--src/relooper/test.txt4
-rw-r--r--src/relooper/test2.txt15
-rw-r--r--src/relooper/test3.txt38
-rw-r--r--src/relooper/test4.txt21
-rw-r--r--src/relooper/test6.txt15
-rw-r--r--src/relooper/test_debug.txt15
-rw-r--r--src/relooper/test_fuzz1.txt13
-rw-r--r--src/relooper/test_fuzz5.txt27
-rw-r--r--src/relooper/test_inf.txt651
-rw-r--r--src/runtime.js5
-rw-r--r--src/settings.js2
-rw-r--r--system/include/emscripten/emscripten.h28
-rw-r--r--system/include/stdbool.h3
-rw-r--r--tests/cases/longjmp_tiny_noasm_invoke.ll71
-rw-r--r--tests/cases/longjmp_tiny_noasm_invoke.txt2
-rwxr-xr-xtests/fuzz/csmith_driver.py13
-rw-r--r--tests/hello_libcxx_mod2.cpp10
-rw-r--r--tests/hello_libcxx_mod2a.cpp11
-rwxr-xr-xtests/runner.py35
-rw-r--r--tests/sdl_audio.c20
-rw-r--r--third_party/jni/emjvm.cpp133
-rw-r--r--third_party/jni/emjvm.h8
-rw-r--r--third_party/jni/emjvm.js180
-rw-r--r--third_party/jni/jni.h1154
-rw-r--r--tools/cache.py194
l---------tools/eliminator/node_modules/.bin/cake1
l---------tools/eliminator/node_modules/.bin/coffee1
-rw-r--r--tools/file_packager.py2
-rw-r--r--tools/js_optimizer.py7
-rw-r--r--tools/jsrun.py27
-rw-r--r--tools/shared.py352
-rw-r--r--tools/tempfiles.py40
41 files changed, 2708 insertions, 782 deletions
diff --git a/.gitignore b/.gitignore
index 31814a09..843b21b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ src/relooper.js.raw.js
src/relooper/*.o
src/relooper/*.out
+tests/fake/ \ No newline at end of file
diff --git a/emcc b/emcc
index b3581ea8..f644b924 100755
--- a/emcc
+++ b/emcc
@@ -421,6 +421,22 @@ Options that are modified or new in %s include:
'jsfuncs' will be cached. So avoid modifying
globals to let caching work fully.
+ To work around the problem mentioned in the
+ previous paragraph, you can use
+
+ emscripten_jcache_printf
+
+ when adding debug printfs to your code. That
+ function is specially preprocessed so that it
+ does not create a constant string global for
+ its first argument. See emscripten.h for more
+ details. Note in particular that you need to
+ already have a call to that function in your
+ code *before* you add one and do an incremental
+ build, so that adding an external reference
+ (also a global property) does not invalidate
+ everything.
+
--clear-cache Manually clears the cache of compiled
emscripten system libraries (libc++,
libc++abi, libc). This is normally
@@ -978,6 +994,7 @@ try:
for input_file in input_files:
if input_file.endswith(SOURCE_SUFFIXES):
if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file
+ input_file = shared.Building.preprocess(input_file, in_temp(uniquename(input_file)))
output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o')
temp_files.append(output_file)
args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file]
diff --git a/emscripten.py b/emscripten.py
index 1fc5f190..08ae85c5 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -9,21 +9,9 @@ header files (so that the JS compiler can see the constants in those
headers, for the libc implementation in JS).
'''
-import os, sys, json, optparse, subprocess, re, time, multiprocessing
+import os, sys, json, optparse, subprocess, re, time, multiprocessing, functools
-if not os.environ.get('EMSCRIPTEN_SUPPRESS_USAGE_WARNING'):
- print >> sys.stderr, '''
-==============================================================
-WARNING: You should normally never use this! Use emcc instead.
-==============================================================
- '''
-
-from tools import shared
-
-DEBUG = os.environ.get('EMCC_DEBUG')
-if DEBUG == "0":
- DEBUG = None
-DEBUG_CACHE = DEBUG and "cache" in DEBUG
+from tools import jsrun, cache as cache_module, tempfiles
__rootpath__ = os.path.abspath(os.path.dirname(__file__))
def path_from_root(*pathelems):
@@ -32,11 +20,6 @@ def path_from_root(*pathelems):
"""
return os.path.join(__rootpath__, *pathelems)
-temp_files = shared.TempFiles()
-
-compiler_engine = None
-jcache = False
-
def scan(ll, settings):
# blockaddress(@main, %23)
blockaddrs = []
@@ -50,16 +33,21 @@ NUM_CHUNKS_PER_CORE = 1.25
MIN_CHUNK_SIZE = 1024*1024
MAX_CHUNK_SIZE = float(os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or 'inf') # configuring this is just for debugging purposes
-def process_funcs(args):
- i, funcs, meta, settings_file, compiler, forwarded_file, libraries = args
+def process_funcs((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files)):
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'))
- shared.try_delete(funcs_file)
+ out = jsrun.run_js(
+ compiler,
+ engine=compiler_engine,
+ args=[settings_file, funcs_file, 'funcs', forwarded_file] + libraries,
+ stdout=subprocess.PIPE,
+ cwd=path_from_root('src'))
+ tempfiles.try_delete(funcs_file)
return out
-def emscript(infile, settings, outfile, libraries=[]):
+def emscript(infile, settings, outfile, libraries=[], compiler_engine=None,
+ jcache=None, temp_files=None, DEBUG=None, DEBUG_CACHE=None):
"""Runs the emscripten LLVM-to-JS compiler. We parallelize as much as possible
Args:
@@ -78,7 +66,7 @@ def emscript(infile, settings, outfile, libraries=[]):
if DEBUG: print >> sys.stderr, 'emscript: ll=>js'
- if jcache: shared.JCache.ensure()
+ if jcache: jcache.ensure()
# Pre-scan ll and alter settings as necessary
if DEBUG: t = time.time()
@@ -147,13 +135,13 @@ def emscript(infile, settings, outfile, libraries=[]):
out = None
if jcache:
keys = [pre_input, settings_text, ','.join(libraries)]
- shortkey = shared.JCache.get_shortkey(keys)
+ shortkey = jcache.get_shortkey(keys)
if DEBUG_CACHE: print >>sys.stderr, 'shortkey', shortkey
- out = shared.JCache.get(shortkey, keys)
+ out = jcache.get(shortkey, keys)
if DEBUG_CACHE and not out:
- dfpath = os.path.join(shared.TEMP_DIR, "ems_" + shortkey)
+ dfpath = os.path.join(configuration.TEMP_DIR, "ems_" + shortkey)
dfp = open(dfpath, 'w')
dfp.write(pre_input);
dfp.write("\n\n========================== settings_text\n\n");
@@ -166,10 +154,11 @@ def emscript(infile, settings, outfile, libraries=[]):
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'))
+ out = jsrun.run_js(compiler, 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)
+ 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)
@@ -194,15 +183,17 @@ def emscript(infile, settings, outfile, libraries=[]):
settings['EXPORTED_FUNCTIONS'] = forwarded_json['EXPORTED_FUNCTIONS']
save_settings()
- chunks = shared.JCache.chunkify(funcs, chunk_size, 'emscript_files' if jcache else None)
+ chunks = cache_module.chunkify(
+ funcs, chunk_size,
+ jcache.get_cachename('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?)
+ shortkey = jcache.get_shortkey(keys) # TODO: share shortkeys with later code
+ out = jcache.get(shortkey, keys) # this is relatively expensive (pickling?)
if out:
cached_outputs.append(out)
return False
@@ -215,12 +206,16 @@ def emscript(infile, settings, outfile, libraries=[]):
# 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 cores == 1 and total_ll_size < MAX_CHUNK_SIZE:
+ assert len(chunks) == 1, 'no point in splitting up without multiple cores'
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))]
+ commands = [
+ (i, chunk, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files)
+ for i, chunk in enumerate(chunks)
+ ]
if len(chunks) > 1:
pool = multiprocessing.Pool(processes=cores)
@@ -235,15 +230,15 @@ def emscript(infile, settings, outfile, libraries=[]):
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])
+ shortkey = jcache.get_shortkey(keys)
+ 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]
for output in outputs:
- assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[0]
+ assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[0][-300:]
if DEBUG: print >> sys.stderr, ' emscript: phase 2 took %s seconds' % (time.time() - t)
if DEBUG: t = time.time()
@@ -311,7 +306,8 @@ def emscript(infile, settings, outfile, libraries=[]):
if DEBUG: t = time.time()
post_file = temp_files.get('.post.ll').name
open(post_file, 'w').write('\n') # no input, just processing of forwarded data
- out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src'))
+ out = jsrun.run_js(compiler, compiler_engine, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE,
+ cwd=path_from_root('src'))
post, last_forwarded_data = out.split('//FORWARDED_DATA:') # if this fails, perhaps the process failed prior to printing forwarded data?
last_forwarded_json = json.loads(last_forwarded_data)
@@ -495,8 +491,7 @@ Runtime.stackRestore = function(top) { asm.stackRestore(top) };
outfile.close()
-
-def main(args):
+def main(args, compiler_engine, cache, jcache, relooper, temp_files, DEBUG, DEBUG_CACHE):
# Prepare settings for serialization to JSON.
settings = {}
for setting in args.settings:
@@ -570,16 +565,23 @@ def main(args):
libraries = args.libraries[0].split(',') if len(args.libraries) > 0 else []
# Compile the assembly to Javascript.
- if settings.get('RELOOP'): shared.Building.ensure_relooper()
-
- emscript(args.infile, settings, args.outfile, libraries)
-
-if __name__ == '__main__':
+ if settings.get('RELOOP'):
+ if not relooper:
+ relooper = cache.get_path('relooper.js')
+ settings.setdefault('RELOOPER', relooper)
+ if not os.path.exists(relooper):
+ from tools import shared
+ shared.Building.ensure_relooper(relooper)
+
+ emscript(args.infile, settings, args.outfile, libraries, compiler_engine=compiler_engine,
+ jcache=jcache, temp_files=temp_files, DEBUG=DEBUG, DEBUG_CACHE=DEBUG_CACHE)
+
+def _main(environ):
parser = optparse.OptionParser(
- usage='usage: %prog [-h] [-H HEADERS] [-o OUTFILE] [-c COMPILER_ENGINE] [-s FOO=BAR]* infile',
- description=('You should normally never use this! Use emcc instead. '
- 'This is a wrapper around the JS compiler, converting .ll to .js.'),
- epilog='')
+ usage='usage: %prog [-h] [-H HEADERS] [-o OUTFILE] [-c COMPILER_ENGINE] [-s FOO=BAR]* infile',
+ description=('You should normally never use this! Use emcc instead. '
+ 'This is a wrapper around the JS compiler, converting .ll to .js.'),
+ epilog='')
parser.add_option('-H', '--headers',
default=[],
action='append',
@@ -592,8 +594,11 @@ if __name__ == '__main__':
default=sys.stdout,
help='Where to write the output; defaults to stdout.')
parser.add_option('-c', '--compiler',
- default=shared.COMPILER_ENGINE,
+ default=None,
help='Which JS engine to use to run the compiler; defaults to the one in ~/.emscripten.')
+ parser.add_option('--relooper',
+ default=None,
+ help='Which relooper file to use if RELOOP is enabled.')
parser.add_option('-s', '--setting',
dest='settings',
default=[],
@@ -605,16 +610,82 @@ if __name__ == '__main__':
action='store_true',
default=False,
help=('Enable jcache (ccache-like caching of compilation results, for faster incremental builds).'))
+ parser.add_option('-T', '--temp-dir',
+ default=None,
+ help=('Where to create temporary files.'))
+ parser.add_option('-v', '--verbose',
+ action='store_true',
+ dest='verbose',
+ help='Displays debug output')
+ parser.add_option('-q', '--quiet',
+ action='store_false',
+ dest='verbose',
+ help='Hides debug output')
+ parser.add_option('--suppressUsageWarning',
+ action='store_true',
+ default=environ.get('EMSCRIPTEN_SUPPRESS_USAGE_WARNING'),
+ help=('Suppress usage warning'))
# Convert to the same format that argparse would have produced.
keywords, positional = parser.parse_args()
+
+ if not keywords.suppressUsageWarning:
+ print >> sys.stderr, '''
+==============================================================
+WARNING: You should normally never use this! Use emcc instead.
+==============================================================
+ '''
+
if len(positional) != 1:
raise RuntimeError('Must provide exactly one positional argument.')
keywords.infile = os.path.abspath(positional[0])
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))
+ if keywords.relooper:
+ relooper = os.path.abspath(keywords.relooper)
+ else:
+ relooper = None # use the cache
+
+ def get_configuration():
+ if hasattr(get_configuration, 'configuration'):
+ return get_configuration.configuration
+
+ from tools import shared
+ configuration = shared.Configuration(environ=os.environ)
+ get_configuration.configuration = configuration
+ return configuration
+
+ if keywords.temp_dir is None:
+ temp_files = get_configuration().get_temp_files()
+ else:
+ temp_dir = os.path.abspath(keywords.temp_dir)
+ if not os.path.exists(temp_dir):
+ os.makedirs(temp_dir)
+ temp_files = tempfiles.TempFiles(temp_dir)
+
+ if keywords.compiler is None:
+ from tools import shared
+ keywords.compiler = shared.COMPILER_ENGINE
+
+ if keywords.verbose is None:
+ DEBUG = get_configuration().DEBUG
+ DEBUG_CACHE = get_configuration().DEBUG_CACHE
+ else:
+ DEBUG = keywords.verbose
+ DEBUG_CACHE = keywords.verbose
+
+ cache = cache_module.Cache()
+ temp_files.run_and_clean(lambda: main(
+ keywords,
+ compiler_engine=keywords.compiler,
+ cache=cache,
+ jcache=cache_module.JCache(cache) if keywords.jcache else None,
+ relooper=relooper,
+ temp_files=temp_files,
+ DEBUG=DEBUG,
+ DEBUG_CACHE=DEBUG_CACHE,
+ ))
+if __name__ == '__main__':
+ _main(environ=os.environ)
diff --git a/src/analyzer.js b/src/analyzer.js
index ecb5ea6b..dbbb267d 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1380,7 +1380,7 @@ function analyzer(data, sidePass) {
var label = func.labels[i];
for (var j = 0; j < label.lines.length; j++) {
var line = label.lines[j];
- if (line.intertype == 'call' && line.ident == setjmp) {
+ if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
var oldIdent = label.ident;
var newIdent = func.labelIdCounter++;
diff --git a/src/compiler.js b/src/compiler.js
index 1cd09c30..3047daf1 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -199,7 +199,7 @@ load('parseTools.js');
load('intertyper.js');
load('analyzer.js');
load('jsifier.js');
-if (RELOOP) load('relooper.js')
+if (RELOOP) load(RELOOPER)
globalEval(processMacros(preprocess(read('runtime.js'))));
Runtime.QUANTUM_SIZE = QUANTUM_SIZE;
diff --git a/src/jsifier.js b/src/jsifier.js
index ca404258..ff58ece2 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1210,7 +1210,7 @@ function JSify(data, functionsOnly, givenFunctions) {
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, null, null, null, null, ',') + ' : 0),tempValue)';
+ return '(tempValue=' + makeGetValue(param1, 0, type) + ',(' + makeGetValue(param1, 0, type) + '==(' + param2 + '|0) ? ' + makeSetValue(param1, 0, param3, type, null, null, null, null, ',') + ' : 0),tempValue)';
}
default: throw 'unhandled atomic op: ' + item.op;
}
diff --git a/src/library.js b/src/library.js
index 5b1ed33b..a5166c72 100644
--- a/src/library.js
+++ b/src/library.js
@@ -2403,6 +2403,7 @@ LibraryManager.library = {
case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16;
case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6;
case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4;
+ case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: return 1;
}
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
@@ -6072,6 +6073,15 @@ LibraryManager.library = {
__timespec_struct_layout: Runtime.generateStructInfo([
['i32', 'tv_sec'],
['i32', 'tv_nsec']]),
+ nanosleep__deps: ['usleep', '__timespec_struct_layout'],
+ nanosleep: function(rqtp, rmtp) {
+ // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+ var seconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_sec', 'i32') }}};
+ var nanoseconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_nsec', 'i32') }}};
+ {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}}
+ {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
+ return _usleep((seconds * 1e6) + (nanoseconds / 1000));
+ },
// TODO: Implement these for real.
clock_gettime__deps: ['__timespec_struct_layout'],
clock_gettime: function(clk_id, tp) {
@@ -6701,6 +6711,13 @@ LibraryManager.library = {
},
// ==========================================================================
+ // sched.h (stubs only - no thread support yet!)
+ // ==========================================================================
+ sched_yield: function() {
+ return 0;
+ },
+
+ // ==========================================================================
// pthread.h (stubs for mutexes only - no thread support yet!)
// ==========================================================================
@@ -6716,8 +6733,15 @@ LibraryManager.library = {
},
pthread_cond_init: function() {},
pthread_cond_destroy: function() {},
- pthread_cond_broadcast: function() {},
- pthread_cond_wait: function() {},
+ pthread_cond_broadcast: function() {
+ return 0;
+ },
+ pthread_cond_wait: function() {
+ return 0;
+ },
+ pthread_cond_timedwait: function() {
+ return 0;
+ },
pthread_self: function() {
//FIXME: assumes only a single thread
return 0;
@@ -7376,6 +7400,23 @@ LibraryManager.library = {
emscripten_random: function() {
return Math.random();
},
+
+ emscripten_jcache_printf___deps: ['_formatString'],
+ emscripten_jcache_printf_: function(varargs) {
+ var MAX = 10240;
+ if (!_emscripten_jcache_printf_.buffer) {
+ _emscripten_jcache_printf_.buffer = _malloc(MAX);
+ }
+ var i = 0;
+ do {
+ var curr = {{{ makeGetValue('varargs', 'i*4', 'i8') }}};
+ {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}};
+ i++;
+ assert(i*4 < MAX);
+ } while (curr != 0);
+ Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs + i*4)).replace('\\n', ''));
+ Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free
+ },
};
function autoAddDeps(object, name) {
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 96ae6fa2..d707a8bf 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1463,10 +1463,70 @@ var LibrarySDL = {
return (SDL.music.audio && !SDL.music.audio.paused) ? 1 : 0;
},
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_38.html#SEC38
+ // "Note: Does not check if the channel has been paused."
+ Mix_Playing: function(id) {
+ if (id === -1) {
+ var count = 0;
+ for (var i = 0; i < SDL.audios.length; i++) {
+ count += SDL.Mix_Playing(i);
+ }
+ return count;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio && !info.audio.paused) {
+ return 1;
+ }
+ return 0;
+ },
+
+ Mix_Pause: function(id) {
+ if (id === -1) {
+ for (var i = 0; i<SDL.audios.length;i++) {
+ SDL.Mix_Pause(i);
+ }
+ return;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio) {
+ info.audio.pause();
+ }
+ },
+
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_39.html#SEC39
+ Mix_Paused: function(id) {
+ if (id === -1) {
+ var pausedCount = 0;
+ for (var i = 0; i<SDL.audios.length;i++) {
+ pausedCount += SDL.Mix_Paused(i);
+ }
+ return pausedCount;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio && info.audio.paused) {
+ return 1;
+ }
+ return 0;
+ },
+
Mix_PausedMusic: function() {
return (SDL.music.audio && SDL.music.audio.paused) ? 1 : 0;
},
+ // http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_33.html#SEC33
+ Mix_Resume: function(id) {
+ if (id === -1) {
+ for (var i = 0; i<SDL.audios.length;i++) {
+ SDL.Mix_Resume(i);
+ }
+ return;
+ }
+ var info = SDL.audios[id];
+ if (info && info.audio) {
+ info.audio.play();
+ }
+ },
+
// SDL TTF
TTF_Init: function() { return 0 },
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index ae8577b1..1a7acc15 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -909,48 +909,54 @@ void Relooper::Calculate(Block *Entry) {
}
std::stack<Shape*> &LoopStack = *((std::stack<Shape*>*)Closure);
- SHAPE_SWITCH(Root, {
- MultipleShape *Fused = Shape::IsMultiple(Root->Next);
- // If we are fusing a Multiple with a loop into this Simple, then visit it now
- if (Fused && Fused->NeedLoop) {
- LoopStack.push(Fused);
- RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused);
- }
- for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
- Block *Target = iter->first;
- Branch *Details = iter->second;
- if (Details->Type != Branch::Direct) {
- assert(LoopStack.size() > 0);
- if (Details->Ancestor != LoopStack.top()) {
- LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
- Labeled->Labeled = true;
- Details->Labeled = true;
- } else {
- Details->Labeled = false;
+ Shape *Next = Root;
+ while (Next) {
+ Root = Next;
+ Next = NULL;
+
+ SHAPE_SWITCH(Root, {
+ MultipleShape *Fused = Shape::IsMultiple(Root->Next);
+ // If we are fusing a Multiple with a loop into this Simple, then visit it now
+ if (Fused && Fused->NeedLoop) {
+ LoopStack.push(Fused);
+ RECURSE_MULTIPLE_MANUAL(FindLabeledLoops, Fused);
+ }
+ for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
+ Block *Target = iter->first;
+ Branch *Details = iter->second;
+ if (Details->Type != Branch::Direct) {
+ assert(LoopStack.size() > 0);
+ if (Details->Ancestor != LoopStack.top()) {
+ LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
+ Labeled->Labeled = true;
+ Details->Labeled = true;
+ } else {
+ Details->Labeled = false;
+ }
}
}
- }
- if (Fused && Fused->NeedLoop) {
- LoopStack.pop();
- if (Fused->Next) FindLabeledLoops(Fused->Next);
- } else {
- if (Root->Next) FindLabeledLoops(Root->Next);
-