aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS4
-rwxr-xr-xemcc26
-rwxr-xr-xemscripten.py47
-rw-r--r--src/analyzer.js43
-rw-r--r--src/embind/embind.js26
-rw-r--r--src/embind/emval.js15
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js34
-rw-r--r--src/library.js821
-rw-r--r--src/library_gl.js4
-rw-r--r--src/library_sdl.js14
-rw-r--r--src/modules.js19
-rw-r--r--src/parseTools.js10
-rw-r--r--src/preamble.js1
-rw-r--r--src/relooper/Relooper.cpp2
-rw-r--r--src/runtime.js14
-rw-r--r--src/settings.js12
-rw-r--r--system/include/emscripten/bind.h56
-rw-r--r--system/include/libc/sys/stat.h3
-rw-r--r--system/include/libc/sys/types.h4
-rw-r--r--system/include/net/netinet/in.h7
-rw-r--r--system/include/sys/socket.h3
-rw-r--r--tests/cases/entry3.ll36
-rw-r--r--tests/cases/entry3.txt2
-rw-r--r--tests/cases/i32_mem.ll23
-rw-r--r--tests/cases/i32_mem.txt2
-rw-r--r--tests/cases/sillyfuncast.ll23
-rw-r--r--tests/cases/sillyfuncast2.ll21
-rw-r--r--tests/cases/zeroembedded.ll23
-rw-r--r--tests/cases/zeroembedded.txt1
-rw-r--r--tests/embind/embind.test.js19
-rw-r--r--tests/embind/embind_test.cpp24
-rw-r--r--tests/glgetattachedshaders.c93
-rwxr-xr-xtests/runner.py548
-rw-r--r--tests/sdl_rotozoom.c3
-rw-r--r--tests/sdl_rotozoom.pngbin437956 -> 431921 bytes
-rw-r--r--tests/stat/output.txt202
-rw-r--r--tests/stat/src.c242
-rw-r--r--tests/stat/test_chmod.c153
-rw-r--r--tests/stat/test_mknod.c96
-rw-r--r--tests/stat/test_stat.c167
-rw-r--r--tests/unistd/unlink.c165
-rw-r--r--tests/unistd/unlink.js7
-rw-r--r--tests/unistd/unlink.out42
-rw-r--r--tools/file_packager.py21
-rw-r--r--tools/find_bigfuncs.py13
-rw-r--r--tools/js-optimizer.js467
-rw-r--r--tools/js_optimizer.py24
-rw-r--r--tools/shared.py2
-rw-r--r--tools/test-js-optimizer-asm-outline-output.js570
-rw-r--r--tools/test-js-optimizer-asm-outline1-output.js566
-rw-r--r--tools/test-js-optimizer-asm-outline1.js234
-rw-r--r--tools/test-js-optimizer-asm-outline2-output.js728
-rw-r--r--tools/test-js-optimizer-asm-outline2.js (renamed from tools/test-js-optimizer-asm-outline.js)2
-rw-r--r--tools/test-js-optimizer-asm-pre-output.js8
55 files changed, 4190 insertions, 1504 deletions
diff --git a/AUTHORS b/AUTHORS
index 752e7593..ea4c8f0b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -87,3 +87,7 @@ a license to everyone to use it as detailed in LICENSE.)
* Manfred Manik Nerurkar <nerurkar*at*made-apps.biz> (copyright owned by MADE, GmbH)
* Joseph Gentle <me@josephg.com>
* Douglas T. Crosher <dtc-moz@scieneer.com> (copyright owned by Mozilla Founcation)
+* Soeren Balko <soeren.balko@gmail.com>
+* Ryan Kelly (ryan@rfk.id.au)
+* Michael Lelli <toadking@toadking.com>
+
diff --git a/emcc b/emcc
index bffe8d5d..91b109b6 100755
--- a/emcc
+++ b/emcc
@@ -302,6 +302,9 @@ Options that are modified or new in %s include:
your main compiled code (or run it before in
some other way).
+ For more docs on the options --preload-file
+ accepts, see https://github.com/kripken/emscripten/wiki/Filesystem-Guide
+
--compression <codec> Compress both the compiled code and embedded/
preloaded files. <codec> should be a triple,
@@ -1046,7 +1049,7 @@ try:
if shared.Settings.CORRECT_SIGNS >= 2 or shared.Settings.CORRECT_OVERFLOWS >= 2 or shared.Settings.CORRECT_ROUNDINGS >= 2:
debug_level = 4 # must keep debug info to do line-by-line operations
- if debug_level > 0 and closure:
+ if debug_level > 1 and closure:
logging.warning('disabling closure because debug info was requested')
closure = False
@@ -1077,6 +1080,9 @@ try:
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules
debug_level = max(debug_level, 2)
+ if shared.Settings.DLOPEN_SUPPORT:
+ shared.Settings.LINKABLE = 1
+
## Compile source code to bitcode
logging.debug('compiling to bitcode')
@@ -1535,14 +1541,17 @@ try:
# It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing
js_optimizer_queue = []
+ js_optimizer_extra_info = {}
def flush_js_optimizer_queue():
- global final, js_optimizer_queue
+ global final, js_optimizer_queue, js_optimizer_extra_info
+ if len(js_optimizer_extra_info) == 0:
+ js_optimizer_extra_info = None
if len(js_optimizer_queue) > 0 and not(len(js_optimizer_queue) == 1 and js_optimizer_queue[0] == 'last'):
if DEBUG != '2':
if shared.Settings.ASM_JS:
js_optimizer_queue = ['asm'] + js_optimizer_queue
logging.debug('applying js optimization passes: %s', js_optimizer_queue)
- final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4)
+ final = shared.Building.js_optimizer(final, js_optimizer_queue, jcache, debug_level >= 4, js_optimizer_extra_info)
js_transform_tempfiles.append(final)
if DEBUG: save_intermediate('js_opts')
else:
@@ -1551,10 +1560,11 @@ try:
if shared.Settings.ASM_JS:
passes = ['asm'] + passes
logging.debug('applying js optimization pass: %s', passes)
- final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4)
+ final = shared.Building.js_optimizer(final, passes, jcache, debug_level >= 4, js_optimizer_extra_info)
js_transform_tempfiles.append(final)
save_intermediate(name)
js_optimizer_queue = []
+ js_optimizer_extra_info = {}
if opt_level >= 1:
logging.debug('running pre-closure post-opts')
@@ -1571,7 +1581,7 @@ try:
else:
return 'eliminate'
- js_optimizer_queue += [get_eliminate(), 'simplifyExpressionsPre']
+ js_optimizer_queue += [get_eliminate(), 'simplifyExpressions']
if shared.Settings.RELOOP and not shared.Settings.ASM_JS:
js_optimizer_queue += ['optimizeShiftsAggressive', get_eliminate()] # aggressive shifts optimization requires loops, it breaks on switches
@@ -1585,9 +1595,9 @@ try:
final = shared.Building.closure_compiler(final)
if DEBUG: save_intermediate('closure')
- if opt_level >= 1:
- logging.debug('running post-closure post-opts')
- js_optimizer_queue += ['simplifyExpressionsPost']
+ if shared.Settings.OUTLINING_LIMIT > 0:
+ js_optimizer_queue += ['outline']
+ js_optimizer_extra_info['sizeToOutline'] = shared.Settings.OUTLINING_LIMIT
if (not closure or shared.Settings.ASM_JS) and shared.Settings.RELOOP and debug_level < 3:
js_optimizer_queue += ['registerize']
diff --git a/emscripten.py b/emscripten.py
index d9367566..3e3538e9 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -9,7 +9,7 @@ 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, functools
+import os, sys, json, optparse, subprocess, re, time, multiprocessing, string
from tools import shared
from tools import jsrun, cache as cache_module, tempfiles
@@ -44,20 +44,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((i, funcs, meta, settings_file, compiler, forwarded_file, libraries, compiler_engine, temp_files, DEBUG)):
- funcs_file = temp_files.get('.func_%d.ll' % i).name
- f = open(funcs_file, 'w')
- f.write(funcs)
- funcs = None
- f.write('\n')
- f.write(meta)
- f.close()
- 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)
+ try:
+ funcs_file = temp_files.get('.func_%d.ll' % i).name
+ f = open(funcs_file, 'w')
+ f.write(funcs)
+ funcs = None
+ f.write('\n')
+ f.write(meta)
+ f.close()
+ 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'))
+ except KeyboardInterrupt:
+ # Python 2.7 seems to lock up when a child process throws KeyboardInterrupt
+ raise Exception()
+ finally:
+ tempfiles.try_delete(funcs_file)
if DEBUG: print >> sys.stderr, '.'
return out
@@ -620,6 +625,18 @@ Runtime.stackRestore = function(top) { asm['stackRestore'](top) };
// EMSCRIPTEN_END_FUNCS
''']
+ # Create symbol table for self-dlopen
+ if settings.get('DLOPEN_SUPPORT'):
+ symbol_table = { k:v+forwarded_json['Runtime']['GLOBAL_BASE']
+ for k,v in forwarded_json['Variables']['indexedGlobals'].iteritems()
+ if forwarded_json['Variables']['globals'][k]['named'] }
+ for raw in last_forwarded_json['Functions']['tables'].itervalues():
+ if raw == '': continue
+ table = map(string.strip, raw[raw.find('[')+1:raw.find(']')].split(","))
+ symbol_table.update(map(lambda x: (x[1], x[0]),
+ filter(lambda x: x[1] != '0', enumerate(table))))
+ outfile.write("var SYMBOL_TABLE = %s;" % json.dumps(symbol_table))
+
for funcs_js_item in funcs_js: # do this loop carefully to save memory
funcs_js_item = indexize(funcs_js_item)
funcs_js_item = blockaddrsize(funcs_js_item)
diff --git a/src/analyzer.js b/src/analyzer.js
index de9a7940..b1f0b585 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -502,18 +502,38 @@ function analyzer(data, sidePass) {
{ intertype: 'value', ident: j.toString(), type: 'i32' }
]
});
- var actualSizeType = 'i' + element.bits; // The last one may be smaller than 32 bits
- toAdd.push({
+ var newItem = {
intertype: 'load',
assignTo: element.ident,
- pointerType: actualSizeType + '*',
- valueType: actualSizeType,
- type: actualSizeType, // XXX why is this missing from intertyper?
- pointer: { intertype: 'value', ident: tempVar, type: actualSizeType + '*' },
+ pointerType: 'i32*',
+ valueType: 'i32',
+ type: 'i32',
+ pointer: { intertype: 'value', ident: tempVar, type: 'i32*' },
ident: tempVar,
- pointerType: actualSizeType + '*',
align: value.align
- });
+ };
+ var newItem2 = null;
+ // The last one may be smaller than 32 bits
+ if (element.bits < 32) {
+ newItem.assignTo += '$preadd$';
+ newItem2 = {
+ intertype: 'mathop',
+ op: 'and',
+ assignTo: element.ident,
+ type: 'i32',
+ params: [{
+ intertype: 'value',
+ type: 'i32',
+ ident: newItem.assignTo
+ }, {
+ intertype: 'value',
+ type: 'i32',
+ ident: (0xffffffff >>> (32 - element.bits)).toString()
+ }],
+ };
+ }
+ toAdd.push(newItem);
+ if (newItem2) toAdd.push(newItem2);
j++;
});
Types.needAnalysis['[0 x i32]'] = 0;
@@ -1433,15 +1453,14 @@ function analyzer(data, sidePass) {
func.labelsDict = {};
func.labelIds = {};
func.labelIdsInverse = {};
- func.labelIds[toNiceIdent('%0')] = 1;
- func.labelIdsInverse[0] = toNiceIdent('%0');
- func.labelIdCounter = 2;
+ func.labelIdCounter = 1;
func.labels.forEach(function(label) {
if (!(label.ident in func.labelIds)) {
func.labelIds[label.ident] = func.labelIdCounter++;
func.labelIdsInverse[func.labelIdCounter-1] = label.ident;
}
});
+ var entryIdent = func.labels[0].ident;
// Minify label ids to numeric ids.
func.labels.forEach(function(label) {
@@ -1478,7 +1497,7 @@ function analyzer(data, sidePass) {
function getActualLabelId(labelId) {
if (func.labelsDict[labelId]) return labelId;
// If not present, it must be a surprisingly-named entry (or undefined behavior, in which case, still ok to use the entry)
- labelId = func.labelIds[ENTRY_IDENT];
+ labelId = func.labelIds[entryIdent];
assert(func.labelsDict[labelId]);
return labelId;
}
diff --git a/src/embind/embind.js b/src/embind/embind.js
index 91386c69..f0cd0c74 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -5,9 +5,9 @@
/*global __emval_register, _emval_handle_array, __emval_decref*/
/*global ___getTypeName*/
/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */
-var InternalError = Module.InternalError = extendError(Error, 'InternalError');
-var BindingError = Module.BindingError = extendError(Error, 'BindingError');
-var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError');
+var InternalError = Module['InternalError'] = extendError(Error, 'InternalError');
+var BindingError = Module['BindingError'] = extendError(Error, 'BindingError');
+var UnboundTypeError = Module['UnboundTypeError'] = extendError(BindingError, 'UnboundTypeError');
function throwInternalError(message) {
throw new InternalError(message);
@@ -638,7 +638,7 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
var tupleRegistrations = {};
-function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) {
+function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) {
tupleRegistrations[rawType] = {
name: readLatin1String(name),
rawConstructor: FUNCTION_TABLE[rawConstructor],
@@ -647,7 +647,7 @@ function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) {
};
}
-function __embind_register_tuple_element(
+function __embind_register_value_array_element(
rawTupleType,
getterReturnType,
getter,
@@ -666,7 +666,7 @@ function __embind_register_tuple_element(
});
}
-function __embind_finalize_tuple(rawTupleType) {
+function __embind_finalize_value_array(rawTupleType) {
var reg = tupleRegistrations[rawTupleType];
delete tupleRegistrations[rawTupleType];
var elements = reg.elements;
@@ -725,7 +725,7 @@ function __embind_finalize_tuple(rawTupleType) {
var structRegistrations = {};
-function __embind_register_struct(
+function __embind_register_value_object(
rawType,
name,
rawConstructor,
@@ -739,7 +739,7 @@ function __embind_register_struct(
};
}
-function __embind_register_struct_field(
+function __embind_register_value_object_field(
structType,
fieldName,
getterReturnType,
@@ -760,7 +760,7 @@ function __embind_register_struct_field(
});
}
-function __embind_finalize_struct(structType) {
+function __embind_finalize_value_object(structType) {
var reg = structRegistrations[structType];
delete structRegistrations[structType];
@@ -879,11 +879,11 @@ var genericPointerToWireType = function(destructors, handle) {
if (handle.$$.smartPtrType === this) {
ptr = handle.$$.smartPtr;
} else {
- var clonedHandle = handle.clone();
+ var clonedHandle = handle['clone']();
ptr = this.rawShare(
ptr,
__emval_register(function() {
- clonedHandle.delete();
+ clonedHandle['delete']();
})
);
if (destructors !== null) {
@@ -1088,7 +1088,7 @@ function getInstanceTypeName(handle) {
return handle.$$.ptrType.registeredClass.name;
}
-ClassHandle.prototype.isAliasOf = function(other) {
+ClassHandle.prototype['isAliasOf'] = function(other) {
if (!(this instanceof ClassHandle)) {
return false;
}
@@ -1118,7 +1118,7 @@ function throwInstanceAlreadyDeleted(obj) {
throwBindingError(getInstanceTypeName(obj) + ' instance already deleted');
}
-ClassHandle.prototype.clone = function() {
+ClassHandle.prototype['clone'] = function() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
diff --git a/src/embind/emval.js b/src/embind/emval.js
index 77270597..0d075188 100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -4,6 +4,7 @@
/*global createNamedFunction*/
/*global readLatin1String, writeStringToMemory*/
/*global requireRegisteredType, throwBindingError*/
+/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */
var Module = Module || {};
@@ -100,7 +101,7 @@ function __emval_new_cstring(v) {
function __emval_take_value(type, v) {
type = requireRegisteredType(type, '_emval_take_value');
- v = type.fromWireType(v);
+ v = type['fromWireType'](v);
return __emval_register(v);
}
@@ -203,7 +204,7 @@ function __emval_as(handle, returnType) {
returnType = requireRegisteredType(returnType, 'emval::as');
var destructors = [];
// caller owns destructing
- return returnType.toWireType(destructors, _emval_handle_array[handle].value);
+ return returnType['toWireType'](destructors, _emval_handle_array[handle].value);
}
function parseParameters(argCount, argTypes, argWireTypes) {
@@ -212,7 +213,7 @@ function parseParameters(argCount, argTypes, argWireTypes) {
var argType = requireRegisteredType(
HEAP32[(argTypes >> 2) + i],
"parameter " + i);
- a[i] = argType.fromWireType(argWireTypes[i]);
+ a[i] = argType['fromWireType'](argWireTypes[i]);
}
return a;
}
@@ -223,7 +224,7 @@ function __emval_call(handle, argCount, argTypes) {
var args = new Array(argCount);
for (var i = 0; i < argCount; ++i) {
- args[i] = types[i].fromWireType(arguments[3 + i]);
+ args[i] = types[i]['fromWireType'](arguments[3 + i]);
}
var fn = _emval_handle_array[handle].value;
@@ -247,8 +248,8 @@ function __emval_get_method_caller(argCount, argTypes) {
var retType = types[0];
var signatureName = retType.name + "_$" + types.slice(1).map(function (t) { return t.name; }).join("_") + "$";
- var args1 = ["Runtime", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"];
- var args2 = [Runtime, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType];
+ var args1 = ["addFunction", "createNamedFunction", "requireHandle", "getStringOrSymbol", "_emval_handle_array", "retType"];
+ var args2 = [Runtime.addFunction, createNamedFunction, requireHandle, getStringOrSymbol, _emval_handle_array, retType];
var argsList = ""; // 'arg0, arg1, arg2, ... , argN'
var argsListWired = ""; // 'arg0Wired, ..., argNWired'
@@ -260,7 +261,7 @@ function __emval_get_method_caller(argCount, argTypes) {
}
var invokerFnBody =
- "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" +
+ "return addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" +
"requireHandle(handle);\n" +
"name = getStringOrSymbol(name);\n";
diff --git a/src/intertyper.js b/src/intertyper.js
index 94d937e1..abfbdacb 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -503,6 +503,7 @@ function intertyper(data, sidePass, baseLineNums) {
// variable
var ident = item.tokens[0].text;
var private_ = findTokenText(item, 'private') >= 0 || findTokenText(item, 'internal') >= 0;
+ var named = findTokenText(item, 'unnamed_addr') < 0;
cleanOutTokens(LLVM.GLOBAL_MODIFIERS, item.tokens, [2, 3]);
var external = false;
if (item.tokens[2].text === 'external') {
@@ -516,6 +517,7 @@ function intertyper(data, sidePass, baseLineNums) {
type: item.tokens[2].text,
external: external,
private_: private_,
+ named: named,
lineNum: item.lineNum
};
if (!NAMED_GLOBALS) {
diff --git a/src/jsifier.js b/src/jsifier.js
index 82b78d0a..b377202d 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -286,6 +286,8 @@ function JSify(data, functionsOnly, givenFunctions) {
allocator = 'ALLOC_NONE';
}
+ Variables.globals[item.ident].named = item.named;
+
if (ASM_JS && (MAIN_MODULE || SIDE_MODULE) && !item.private_ && !NAMED_GLOBALS && isIndexableGlobal(item.ident)) {
// We need this to be named (and it normally would not be), so that it can be linked to and used from other modules
Variables.globals[item.ident].linkable = 1;
@@ -602,6 +604,8 @@ function JSify(data, functionsOnly, givenFunctions) {
var associatedSourceFile = "NO_SOURCE";
}
+ if (DLOPEN_SUPPORT) Functions.getIndex(func.ident);
+
func.JS += 'function ' + func.ident + '(' + paramIdents.join(', ') + ') {\n';
if (PGO) {
@@ -1207,7 +1211,7 @@ function JSify(data, functionsOnly, givenFunctions) {
// in an assignment
var disabled = DISABLE_EXCEPTION_CATCHING == 2 && !(item.funcData.ident in EXCEPTION_CATCHING_WHITELIST);
var phiSets = calcPhiSets(item);
- var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone);
+ var call_ = makeFunctionCall(item.ident, item.params, item.funcData, item.type, ASM_JS && !disabled, !!item.assignTo || !item.standalone, true);
var ret;
@@ -1364,7 +1368,7 @@ function JSify(data, functionsOnly, givenFunctions) {
return ret;
});
- function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn) {
+ function makeFunctionCall(ident, params, funcData, type, forceByPointer, hasReturn, invoke) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
@@ -1429,7 +1433,7 @@ function JSify(data, functionsOnly, givenFunctions) {
args = args.map(function(arg, i) { return indexizeFunctions(arg, argsTypes[i]) });
if (ASM_JS) {
- if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || funcData.setjmpTable) {
+ if (shortident in Functions.libraryFunctions || simpleIdent in Functions.libraryFunctions || byPointerForced || invoke || funcData.setjmpTable) {
args = args.map(function(arg, i) { return asmCoercion(arg, argsTypes[i]) });
} else {
args = args.map(function(arg, i) { return asmEnsureFloat(arg, argsTypes[i]) });
@@ -1470,7 +1474,6 @@ function JSify(data, functionsOnly, givenFunctions) {
}
args = args.concat(varargs);
- var argsText = args.join(', ');
// Inline if either we inline whenever we can (and we can), or if there is no noninlined version
var inline = LibraryManager.library[simpleIdent + '__inline'];
@@ -1492,16 +1495,35 @@ function JSify(data, functionsOnly, givenFunctions) {
}
}
+ if (callIdent in Functions.implementedFunctions) {
+ // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as.
+ var numArgs = Functions.implementedFunctions[callIdent].length - 1;
+ if (numArgs !== args.length) {
+ if (VERBOSE) warnOnce('Fixing function call arguments based on signature, on ' + [callIdent, args.length, numArgs]);
+ while (args.length > numArgs) { args.pop(); argsTypes.pop() }
+ while (args.length < numArgs) { args.push('0'); argsTypes.push('i32') }
+ }
+ }
+
var returnType = 'void';
if ((byPointer || ASM_JS) && hasReturn) {
returnType = getReturnType(type);
+ if (callIdent in Functions.implementedFunctions) {
+ // LLVM sometimes bitcasts for no reason. We must call using the exact same type as the actual function is generated as
+ var trueType = Functions.getSignatureReturnType(Functions.implementedFunctions[callIdent]);
+ if (trueType !== returnType && !isIdenticallyImplemented(trueType, returnType)) {
+ if (VERBOSE) warnOnce('Fixing function call based on return type from signature, on ' + [callIdent, returnType, trueType]);
+ returnType = trueType;
+ }
+ }
}
if (byPointer) {
var sig = Functions.getSignature(returnType, argsTypes, hasVarArgs);
if (ASM_JS) {
assert(returnType.search(/\("'\[,/) == -1); // XXX need isFunctionType(type, out)
- if (!byPointerForced && !funcData.setjmpTable) {
+ var functionTableCall = !byPointerForced && !funcData.setjmpTable && !invoke;
+ if (functionTableCall) {
// normal asm function pointer call
callIdent = '(' + callIdent + ')&{{{ FTM_' + sig + ' }}}'; // the function table mask is set in emscripten.py
Functions.neededTables[sig] = 1;
@@ -1516,7 +1538,7 @@ function JSify(data, functionsOnly, givenFunctions) {
assert(!ASM_JS, 'cannot emit safe dyncalls in asm');
callIdent = '(tempInt=' + callIdent + ',tempInt < 0 || tempInt >= FUNCTION_TABLE.length-1 || !FUNCTION_TABLE[tempInt] ? abort("dyncall error: ' + sig + ' " + FUNCTION_TABLE_NAMES[tempInt]) : tempInt)';
}
- if (!ASM_JS || (!byPointerForced && !funcData.setjmpTable)) callIdent = Functions.getTable(sig) + '[' + callIdent + ']';
+ if (!ASM_JS || functionTableCall) callIdent = Functions.getTable(sig) + '[' + callIdent + ']';
}
var ret = callIdent + '(' + args.join(', ') + ')';
diff --git a/src/library.js b/src/library.js
index 20a163c6..e650a545 100644
--- a/src/library.js
+++ b/src/library.js
@@ -559,25 +559,28 @@ LibraryManager.library = {
};
}
var utf8 = new Runtime.UTF8Processor();
- function simpleOutput(val) {
- if (val === null || val === {{{ charCode('\n') }}}) {
- output.printer(output.buffer.join(''));
- output.buffer = [];
- } else {
- output.buffer.push(utf8.processCChar(val));
- }
+ function createSimpleOutput() {
+ var fn = function (val) {
+ if (val === null || val === {{{ charCode('\n') }}}) {
+ fn.printer(fn.buffer.join(''));
+ fn.buffer = [];
+ } else {
+ fn.buffer.push(utf8.processCChar(val));
+ }
+ };
+ return fn;
}
if (!output) {
stdoutOverridden = false;
- output = simpleOutput;
+ output = createSimpleOutput();
}
if (!output.printer) output.printer = Module['print'];
if (!output.buffer) output.buffer = [];
if (!error) {
stderrOverridden = false;
- error = simpleOutput;
+ error = createSimpleOutput();
}
- if (!error.printer) error.printer = Module['print'];
+ if (!error.printer) error.printer = Module['printErr'];
if (!error.buffer) error.buffer = [];
// Create the temporary folder, if not already created
@@ -1045,27 +1048,45 @@ LibraryManager.library = {
mknod: function(path, mode, dev) {
// int mknod(const char *path, mode_t mode, dev_t dev);
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html
- if (dev !== 0 || !(mode & 0xC000)) { // S_IFREG | S_IFDIR.
- // Can't create devices or pipes through mknod().
+ path = Pointer_stringify(path);
+ var fmt = (mode & {{{ cDefine('S_IFMT') }}});
+ if (fmt !== {{{ cDefine('S_IFREG') }}} && fmt !== {{{ cDefine('S_IFCHR') }}} &&
+ fmt !== {{{ cDefine('S_IFBLK') }}} && fmt !== {{{ cDefine('S_IFIFO') }}} &&
+ fmt !== {{{ cDefine('S_IFSOCK') }}}) {
+ // not valid formats for mknod
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
- } else {
- var properties = {contents: [], isFolder: Boolean(mode & 0x4000)}; // S_IFDIR.
- path = FS.analyzePath(Pointer_stringify(path));
- try {
- FS.createObject(path.parentObject, path.name, properties,
- mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR.
- return 0;
- } catch (e) {
- return -1;
- }
+ }
+ if (fmt === {{{ cDefine('S_IFCHR') }}} || fmt === {{{ cDefine('S_IFBLK') }}} ||
+ fmt === {{{ cDefine('S_IFIFO') }}} || fmt === {{{ cDefine('S_IFSOCK') }}}) {
+ // not supported currently
+ ___setErrNo(ERRNO_CODES.EPERM);
+ return -1;
+ }
+ path = FS.analyzePath(path);
+ var properties = { contents: [], isFolder: false }; // S_IFDIR.
+ try {
+ FS.createObject(path.parentObject, path.name, properties,
+ mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR.
+ return 0;
+ } catch (e) {
+ return -1;
}
},
mkdir__deps: ['mknod'],
mkdir: function(path, mode) {
// int mkdir(const char *path, mode_t mode);
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html
- return _mknod(path, 0x4000 | (mode & 0x180), 0); // S_IFDIR, S_IRUSR | S_IWUSR.
+ path = Pointer_stringify(path);
+ path = FS.analyzePath(path);
+ var properties = { contents: [], isFolder: true };
+ try {
+ FS.createObject(path.parentObject, path.name, properties,
+ mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR.
+ return 0;
+ } catch (e) {
+ return -1;
+ }
},
mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'],
mkfifo: function(path, mode) {
@@ -1079,10 +1100,13 @@ LibraryManager.library = {
return -1;
},
chmod__deps: ['$FS'],
- chmod: function(path, mode) {
+ chmod: function(path, mode, dontResolveLastLink) {
// int chmod(const char *path, mode_t mode);
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html
- var obj = FS.findObject(Pointer_stringify(path));
+ // NOTE: dontResolveLastLink is a shortcut for lchmod(). It should never be
+ // used in client code.
+ path = typeof path !== 'string' ? Pointer_stringify(path) : path;
+ var obj = FS.findObject(path, dontResolveLastLink);
if (obj === null) return -1;
obj.read = mode & 0x100; // S_IRUSR.
obj.write = mode & 0x80; // S_IWUSR.
@@ -1093,15 +1117,16 @@ LibraryManager.library = {
fchmod: function(fildes, mode) {
// int fchmod(int fildes, mode_t mode);
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html
- if (!FS.streams[fildes]) {
+ var stream = FS.streams[fildes];
+ if (!stream) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
- } else {
- var pathArray = intArrayFromString(FS.streams[fildes].path);
- return _chmod(allocate(pathArray, 'i8', ALLOC_STACK), mode);
}
+ return _chmod(stream.path, mode);
+ },
+ lchmod: function(path, mode) {
+ return _chmod(path, mode, true);
},
- lchmod: function() { throw 'TODO: lchmod' },
umask__deps: ['$FS'],
umask: function(newMask) {
@@ -1115,6 +1140,7 @@ LibraryManager.library = {
},
stat64: 'stat',
fstat64: 'fstat',
+ lstat64: 'lstat',
__01fstat64_: 'fstat',
__01stat64_: 'stat',
__01lstat64_: 'lstat',
@@ -1775,6 +1801,8 @@ LibraryManager.library = {
} else if (nbyte < 0 || offset < 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
+ } else if (offset >= stream.object.contents.length) {
+ return 0;
} else {
var bytesRead = 0;
while (stream.ungotten.length && nbyte > 0) {
@@ -1784,6 +1812,8 @@ LibraryManager.library = {
}
var contents = stream.object.contents;
var size = Math.min(contents.length - offset, nbyte);
+ assert(size >= 0);
+
#if USE_TYPED_ARRAYS == 2
if (contents.subarray) { // typed array
HEAPU8.set(contents.subarray(offset, offset+size), buf);
@@ -1851,6 +1881,7 @@ LibraryManager.library = {
} else {
var ungotSize = stream.ungotten.length;
bytesRead = _pread(fildes, buf, nbyte, stream.position);
+ assert(bytesRead >= -1);
if (bytesRead != -1) {
stream.position += (stream.ungotten.length - ungotSize) + bytesRead;
}
@@ -1867,42 +1898,42 @@ LibraryManager.library = {
rmdir: function(path) {
// int rmdir(const char *path);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html
- path = FS.analyzePath(Pointer_stringify(path));
+ path = Pointer_stringify(path);
+ path = FS.analyzePath(path, true);
if (!path.parentExists || !path.exists) {
___setErrNo(path.error);
return -1;
- } else if (!path.object.write || path.isRoot) {
+ } else if (!path.parentObject.write) {
___setErrNo(ERRNO_CODES.EACCES);
return -1;
} else if (!path.object.isFolder) {
___setErrNo(ERRNO_CODES.ENOTDIR);
return -1;
+ } else if (path.isRoot || path.path == FS.currentPath) {
+ ___setErrNo(ERRNO_CODES.EBUSY);
+ return -1;
} else {
for (var i in path.object.contents) {
___setErrNo(ERRNO_CODES.ENOTEMPTY);
return -1;
}
- if (path.path == FS.currentPath) {
- ___setErrNo(ERRNO_CODES.EBUSY);
- return -1;
- } else {
- delete path.parentObject.contents[path.name];
- return 0;
- }
+ delete path.parentObject.contents[path.name];
+ return 0;
}
},
unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
unlink: function(path) {
// int unlink(const char *path);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html
- path = FS.analyzePath(Pointer_stringify(path));
+ path = Pointer_stringify(path);
+ path = FS.analyzePath(path, true);
if (!path.parentExists || !path.exists) {
___setErrNo(path.error);
return -1;
} else if (path.object.isFolder) {
- ___setErrNo(ERRNO_CODES.EISDIR);
+ ___setErrNo(ERRNO_CODES.EPERM);
return -1;
- } else if (!path.object.write) {
+ } else if (!path.parentObject.write) {
___setErrNo(ERRNO_CODES.EACCES);
return -1;
} else {
@@ -2559,15 +2590,27 @@ LibraryManager.library = {
continue;
}
- // TODO: Support strings like "%5c" etc.
- if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') {
- var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
- argIndex += Runtime.getAlignSize('void*', null, true);
- fields++;
- next = get();
- {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}}
- formatIndex += 2;
- continue;
+ if (format[formatIndex] === '%') {
+ var nextC = format.indexOf('c', formatIndex+1);
+ if (nextC > 0) {
+ var maxx = 1;
+ if (nextC > formatIndex+1) {
+ var sub = format.substring(formatIndex+1, nextC)
+ maxx = parseInt(sub);
+ if (maxx != sub) maxx = 0;
+ }
+ if (maxx) {
+ var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
+ argIndex += Runtime.getAlignSize('void*', null, true);
+ fields++;
+ for (var i = 0; i < maxx; i++) {
+ next = get();
+ {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}};
+ }
+ formatIndex += nextC - formatIndex + 1;
+ continue;
+ }
+ }
}
// remove whitespace
@@ -3183,7 +3226,7 @@ LibraryManager.library = {
var flush = function(filedes) {
// Right now we write all data directly, except for output devices.
if (FS.streams[filedes] && FS.streams[filedes].object.output) {
- if (!FS.streams[filedes].isTerminal) { // don't flush terminals, it would cause a \n to also appear
+ if (!FS.streams[filedes].object.isTerminal) { // don't flush terminals, it would cause a \n to also appear
FS.streams[filedes].object.output(null);
}
}
@@ -4898,7 +4941,9 @@ LibraryManager.library = {
isprint: function(chr) {
return 0x1F < chr && chr < 0x7F;
},
- isgraph: 'isprint',
+ isgraph: function(chr) {
+ return 0x20 < chr && chr < 0x7F;
+ },
// Lookup tables for glibc ctype implementation.
__ctype_b_loc: function() {
// http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html
@@ -5735,8 +5780,15 @@ LibraryManager.library = {
rintf: 'rint',
lrint: 'rint',
lrintf: 'rint',
+#if USE_TYPED_ARRAYS == 2
+ llrint: function(x) {
+ x = (x < 0) ? -Math.round(-x) : Math.round(x);
+ {{{ makeStructuralReturn(splitI64('x')) }}};
+ },
+#else
llrint: 'rint',
- llrintf: 'rint',
+#endif
+ llrintf: 'llrint',
nearbyint: 'rint',
nearbyintf: 'rint',
trunc: function(x) {
@@ -5878,7 +5930,7 @@ LibraryManager.library = {
dlopen: function(filename, flag) {
// void *dlopen(const char *file, int mode);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
- filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
+ filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
if (DLFCN_DATA.loadedLibNames[filename]) {
// Already loaded; increment ref count and return.
@@ -5887,48 +5939,55 @@ LibraryManager.library = {
return handle;
}
- var target = FS.findObject(filename);
- if (!target || target.isFolder || target.isDevice) {
- DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
- return 0;
+ if (filename === '__self__') {
+ var handle = -1;
+ var lib_module = Module;
+ var cached_functions = SYMBOL_TABLE;
} else {
- FS.forceLoadFile(target);
- var lib_data = intArrayToString(target.contents);
- }
+ var target = FS.findObject(filename);
+ if (!target || target.isFolder || target.isDevice) {
+ DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename;
+ return 0;
+ } else {
+ FS.forceLoadFile(target);
+ var lib_data = intArrayToString(target.contents);
+ }
- try {
- var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
- } catch (e) {
+ try {
+ var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length);
+ } catch (e) {
#if ASSERTIONS
- Module.printErr('Error in loading dynamic library: ' + e);
+ Module.printErr('Error in loading dynamic library: ' + e);
#endif
- DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
- return 0;
- }
+ DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
+ return 0;
+ }
- // Not all browsers support Object.keys().
- var handle = 1;
- for (var key in DLFCN_DATA.loadedLibs) {
- if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
- }
+ // Not all browsers support Object.keys().
+ var handle = 1;
+ for (var key in DLFCN_DATA.loadedLibs) {
+ if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++;
+ }
+
+ // We don't care about RTLD_NOW and RTLD_LAZY.
+ if (flag & 256) { // RTLD_GLOBAL
+ for (var ident in lib_module) {
+ if (lib_module.hasOwnProperty(ident)) {
+ Module[ident] = lib_module[ident];
+ }
+ }
+ }
+ var cached_functions = {};
+ }
DLFCN_DATA.loadedLibs[handle] = {
refcount: 1,
name: filename,
module: lib_module,
- cached_functions: {}
+ cached_functions: cached_functions
};
DLFCN_DATA.loadedLibNames[filename] = handle;
- // We don't care about RTLD_NOW and RTLD_LAZY.
- if (flag & 256) { // RTLD_GLOBAL
- for (var ident in lib_module) {
- if (lib_module.hasOwnProperty(ident)) {
- Module[ident] = lib_module[ident];
- }
- }
- }
-
return handle;
},
// int dlclose(void* handle);
@@ -5941,7 +6000,7 @@ LibraryManager.library = {
return 1;
} else {
var lib_record = DLFCN_DATA.loadedLibs[handle];
- if (lib_record.refcount-- == 0) {
+ if (--lib_record.refcount == 0) {
delete DLFCN_DATA.loadedLibNames[lib_record.name];
delete DLFCN_DATA.loadedLibs[handle];
}
@@ -5960,13 +6019,15 @@ LibraryManager.library = {
return 0;
} else {
var lib = DLFCN_DATA.loadedLibs[handle];
- if (!lib.module.hasOwnProperty(symbol)) {
- DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
- '" in dynamic lib: ' + lib.name);
- return 0;
+ // self-dlopen means that lib.module is not a superset of
+ // cached_functions, so check the latter first
+ if (lib.cached_functions.hasOwnProperty(symbol)) {
+ return lib.cached_functions[symbol];
} else {
- if (lib.cached_functions.hasOwnProperty(symbol)) {
- return lib.cached_functions[symbol];
+ if (!lib.module.hasOwnProperty(symbol)) {
+ DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
+ '" in dynamic lib: ' + lib.name);
+ return 0;
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
@@ -6207,18 +6268,582 @@ LibraryManager.library = {
return -1;
},
- strftime: function(s, maxsize, format, timeptr) {
+ _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+ _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+ _isLeapYear: function(year) {
+ return year%4 === 0 && (year%100 !== 0 || year%400 === 0);
+ },
+
+ _arraySum: function(array, index) {
+ var sum = 0;
+ for (var i = 0; i <= index; sum += array[i++]);
+ return sum;
+ },
+
+ _addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'],
+ _addDays: function(date, days) {
+ var newDate = new Date(date.getTime());
+ while(days > 0) {
+ var leap = __isLeapYear(newDate.getFullYear());
+ var currentMonth = newDate.getMonth();
+ var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth];
+
+ if (days > daysInCurrentMonth-newDate.getDate()) {
+ // we spill over to next month
+ days -= (daysInCurrentMonth-newDate.getDate()+1);
+ newDate.setDate(1);
+ if (currentMonth < 11) {
+ newDate.setMonth(currentMonth+1)
+ } else {
+ newDate.setMonth(0);
+ newDate.setFullYear(newDate.getFullYear()+1);
+ }
+ } else {
+ // we stay in current month
+ newDate.setDate(newDate.getDate()+days);
+ return newDate;
+ }
+ }
+
+ return newDate;
+ },
+
+ strftime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'],
+ strftime: function(s, maxsize, format, tm) {
// size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
- // TODO: Implement.
- return 0;
+
+ var date = {
+ tm_sec: {{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32') }}},
+ tm_min: {{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32') }}},
+ tm_hour: {{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32') }}},
+ tm_mday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32') }}},
+ tm_mon: {{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32') }}},
+ tm_year: {{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32') }}},
+ tm_wday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_wday', 'i32') }}},
+ tm_yday: {{{ makeGetValue('tm', '___tm_struct_layout.tm_yday', 'i32') }}},
+ tm_isdst: {{{ makeGetValue('tm', '___tm_struct_layout.tm_isdst', 'i32') }}}
+ };
+
+ var pattern = Pointer_stringify(format);
+
+ // expand format
+ var EXPANSION_RULES_1 = {
+ '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013
+ '%D': '%m/%d/%y', // Equivalent to %m / %d / %y
+ '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d
+ '%h': '%b', // Equivalent to %b
+ '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation
+ '%R': '%H:%M', // Replaced by the time in 24-hour notation
+ '%T': '%H:%M:%S', // Replaced by the time
+ '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation
+ '%X': '%H:%M:%S', // Replaced by the locale's appropriate date representation
+ };
+ for (var rule in EXPANSION_RULES_1) {
+ pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]);
+ }
+
+ var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+ var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+
+ var leadingSomething = function(value, digits, character) {
+ var str = typeof value === 'number' ? value.toString() : (value || '');
+ while (str.length < digits) {
+ str = character[0]+str;
+ }
+ return str;
+ };
+
+ var leadingNulls = function(value, digits) {
+ return leadingSomething(value, digits, '0');
+ };
+
+ var compareByDay = function(date1, date2) {
+ var sgn = function(value) {
+ return value < 0 ? -1 : (value > 0 ? 1 : 0);
+ };
+
+ var compare;
+ if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) {
+ if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) {
+ compare = sgn(date1.getDate()-date2.getDate());
+ }
+ }
+ return compare;
+ };
+
+ var getFirstWeekStartDate = function(janFourth) {
+ switch (janFourth.getDay()) {
+ case 0: // Sunday
+ return new Date(janFourth.getFullYear()-1, 11, 29);
+ case 1: // Monday
+ return janFourth;
+ case 2: // Tuesday
+ return new Date(janFourth.getFullYear(), 0, 3);
+ case 3: // Wednesday
+ return new Date(janFourth.getFullYear(), 0, 2);
+ case 4: // Thursday
+ return new Date(janFourth.getFullYear(), 0, 1);
+ case 5: // Friday
+ return new Date(janFourth.getFullYear()-1, 11, 31);
+ case 6: // Saturday
+ return new Date(janFourth.getFullYear()-1, 11, 30);
+ }
+ };
+
+ var getWeekBasedYear = function(date) {
+ var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);
+
+ var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4);
+ var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4);
+
+ var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
+ var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
+
+ if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) {
+ // this date is after the start of the first week of this year
+ if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) {
+ return thisDate.getFullYear()+1;
+ } else {
+ return thisDate.getFullYear();
+ }
+ } else {
+ return thisDate.getFullYear()-1;
+ }
+ };
+
+ var EXPANSION_RULES_2 = {
+ '%a': function(date) {
+ return WEEKDAYS[date.tm_wday].substring(0,3);
+ },
+ '%A': function(date) {
+ return WEEKDAYS[date.tm_wday];
+ },
+ '%b': function(date) {
+ return MONTHS[date.tm_mon].substring(0,3);
+ },
+ '%B': function(date) {
+ return MONTHS[date.tm_mon];
+ },
+ '%C': function(date) {
+ var year = date.tm_year+1900;
+ return leadingNulls(Math.floor(year/100),2);
+ },
+ '%d': function(date) {
+ return leadingNulls(date.tm_mday, 2);
+ },
+ '%e': function(date) {
+ return leadingSomething(date.tm_mday, 2, ' ');
+ },
+ '%g': function(date) {
+ // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year.
+ // In this system, weeks begin on a Monday and week 1 of the year is the week that includes
+ // January 4th, which is also the week that includes the first Thursday of the year, and
+ // is also the first week that contains at least four days in the year.
+ // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of
+ // the last week of the preceding year; thus, for Saturday 2nd January 1999,
+ // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th,
+ // or 31st is a Monday, it and any following days are part of week 1 of the following year.
+ // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01.
+
+ return getWeekBasedYear(date).toString().substring(2);
+ },
+ '%G': function(date) {
+ return getWeekBasedYear(date);
+ },
+ '%H': function(date) {
+ return leadingNulls(date.tm_hour, 2);
+ },
+ '%I': function(date) {
+ return leadingNulls(date.tm_hour < 13 ? date.tm_hour : date.tm_hour-12, 2);
+ },
+ '%j': function(date) {
+ // Day of the year (001-366)
+ return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3);
+ },
+ '%m': function(date) {
+ return leadingNulls(date.tm_mon+1, 2);
+ },
+ '%M': function(date) {
+ return leadingNulls(date.tm_min, 2);
+ },
+ '%n': function() {
+ return '\n';
+ },
+ '%p': function(date) {
+ if (date.tm_hour > 0 && date.tm_hour < 13) {
+ return 'AM';
+ } else {
+ return 'PM';
+ }
+ },
+ '%S': function(date) {
+ return leadingNulls(date.tm_sec, 2);
+ },
+ '%t': function() {
+ return '\t';
+ },
+ '%u': function(date) {
+ var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0);
+ return day.getDay() || 7;
+ },
+ '%U': function(date) {
+ // Replaced by the week number of the year as a decimal number [00,53].
+ // The first Sunday of January is the first day of week 1;
+ // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+ var janFirst = new Date(date.tm_year+1900, 0, 1);
+ var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay());
+ var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday);
+
+ // is target date after the first Sunday?
+ if (compareByDay(firstSunday, endDate) < 0) {
+ // calculate difference in days between first Sunday and endDate
+ var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31;
+ var firstSundayUntilEndJanuary = 31-firstSunday.getDate();
+ var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();
+ return leadingNulls(Math.ceil(days/7), 2);
+ }
+
+ return compareByDay(firstSunday, janFirst) === 0 ? '01': '00';
+ },
+ '%V': function(date) {
+ // Replaced by the week number of the year (Monday as the first day of the week)
+ // as a decimal number [01,53]. If the week containing 1 January has four
+ // or more days in the new year, then it is considered week 1.
+ // Otherwise, it is the last week of the previous year, and the next week is week 1.
+ // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
+ var janFourthThisYear = new Date(date.tm_year+1900, 0, 4);
+ var janFourthNextYear = new Date(date.tm_year+1901, 0, 4);
+
+ var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
+ var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
+
+ var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);
+
+ if (compareByDay(endDate, firstWeekStartThisYear) < 0) {
+ // if given date is before this years first week, then it belongs to the 53rd week of last year
+ return '53';
+ }
+
+ if (compareByDay(firstWeekStartNextYear, endDate) <= 0) {
+ // if given date is after next years first week, then it belongs to the 01th week of next year
+ return '01';
+ }
+
+ // given date is in between CW 01..53 of this calendar year
+ var daysDifference;
+ if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) {
+ // first CW of this year starts last year
+ daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate()
+ } else {
+ // first CW of this year starts this year
+ daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate();
+ }
+ return leadingNulls(Math.ceil(daysDifference/7), 2);
+ },
+ '%w': function(date) {
+ var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0);
+ return day.getDay();
+ },
+ '%W': function(date) {
+ // Replaced by the week number of the year as a decimal number [00,53].
+ // The first Monday of January is the first day of week 1;
+ // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
+ var janFirst = new Date(date.tm_year, 0, 1);
+ var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1);
+ var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday);
+
+ // is target date after the first Monday?
+ if (compareByDay(firstMonday, endDate) < 0) {
+ var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31;
+ var firstMondayUntilEndJanuary = 31-firstMonday.getDate();
+ var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();
+ return leadingNulls(Math.ceil(days/7), 2);
+ }
+ return compareByDay(firstMonday, janFirst) === 0 ? '01': '00';
+ },
+ '%y': function(date) {
+ // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year]
+ return (date.tm_year+1900).toString().substring(2);
+ },
+ '%Y': function(date) {
+ // Replaced by the year as a decimal number (for example, 1997). [ tm_year]
+ return date.tm_year+1900;
+ },
+ '%z': function(date) {
+ // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ),
+ // or by no characters if no timezone is determinable.
+ // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich).
+ // If tm_isdst is zero, the standard time offset is used.
+ // If tm_isdst is greater than zero, the daylight savings time offset is used.
+ // If tm_isdst is negative, no characters are returned.
+ // FIXME: we cannot determine time zone (or can we?)
+ return '';
+ },
+ '%Z': function(date) {
+ // Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst]
+ // FIXME: we cannot determine time zone (or can we?)
+ return '';
+ },
+ '%%': function() {
+ return '%';
+ }
+ };
+ for (var rule in EXPANSION_RULES_2) {
+ if (pattern.indexOf(rule) >= 0) {
+ pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date));
+ }
+ }
+
+ var bytes = intArrayFromString(pattern, false);
+ if (bytes.length > maxsize) {
+ return 0;
+ }
+
+ writeArrayToMemory(bytes, s);
+ return bytes.length-1;
},
strftime_l: 'strftime', // no locale support yet
+ strptime__deps: ['__tm_struct_layout', '_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'],
strptime: function(buf, format, tm) {
// char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
- // TODO: Implement.
+ var pattern = Pointer_stringify(format);
+
+ // escape special characters
+ // TODO: not sure we really need to escape all of these in JS regexps
+ var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.';
+ for (var i=0, ii=SPECIAL_CHARS.length; i<ii; ++i) {
+ pattern = pattern.replace(new RegExp('\\'+SPECIAL_CHARS[i], 'g'), '\\'+SPECIAL_CHARS[i]);
+ }
+
+ // reduce number of matchers
+ var EQUIVALENT_MATCHERS = {
+ '%A': '%a',
+ '%B': '%b',
+ '%c': '%x\\s+%X',
+ '%D': '%m\\/%d\\/%y',
+ '%e': '%d',
+ '%h': '%b',
+ '%R': '%H\\:%M',
+ '%r': '%I\\:%M\\:%S\\s%p',
+ '%T': '%H\\:%M\\:%S',
+ '%x': '%m\\/%d\\/(?:%y|%Y)',
+ '%X': '%H\\:%M\\:%S'
+ };
+ for (var matcher in EQUIVALENT_MATCHERS) {
+ pattern = pattern.replace(matcher, EQUIVALENT_MATCHERS[matcher]);
+ }
+
+ // TODO: take care of locale
+
+ var DATE_PATTERNS = {
+ /* weeday name */ '%a': '(?:Sun(?:day)?)|(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)',
+ /* month name */ '%b': '(?:Jan(?:uary)?)|(?:Feb(?:ruary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|May|(?:Jun(?:e)?)|(?:Jul(?:y)?)|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)',
+ /* century */ '%C': '\\d\\d',
+ /* day of month */ '%d': '0[1-9]|[1-9](?!\\d)|1\\d|2\\d|30|31',
+ /* hour (24hr) */ '%H': '\\d(?!\\d)|[0,1]\\d|20|21|22|23',
+ /* hour (12hr) */ '%I': '\\d(?!\\d)|0\\d|10|11|12',
+ /* day of year */ '%j': '00[1-9]|0?[1-9](?!\\d)|0?[1-9]\\d(?!\\d)|[1,2]\\d\\d|3[0-6]\\d',
+ /* month */ '%m': '0[1-9]|[1-9](?!\\d)|10|11|12',
+ /* minutes */ '%M': '0\\d|\\d(?!\\d)|[1-5]\\d',
+ /* whitespace */ '%n': '\\s',
+ /* AM/PM */ '%p': 'AM|am|PM|pm|A\\.M\\.|a\\.m\\.|P\\.M\\.|p\\.m\\.',
+ /* seconds */ '%S': '0\\d|\\d(?!\\d)|[1-5]\\d|60',
+ /* week number */ '%U': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53',
+ /* week number */ '%W': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53',
+ /* weekday number */ '%w': '[0-6]',
+ /* 2-digit year */ '%y': '\\d\\d',
+ /* 4-digit year */ '%Y': '\\d\\d\\d\\d',
+ /* % */ '%%': '%',
+ /* whitespace */ '%t': '\\s',
+ };
+
+ var MONTH_NUMBERS = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11};
+ var DAY_NUMBERS_SUN_FIRST = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6};
+ var DAY_NUMBERS_MON_FIRST = {MON: 0, TUE: 1, WED: 2, THU: 3, FRI: 4, SAT: 5, SUN: 6};
+
+ for (var datePattern in DATE_PATTERNS) {
+ pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')');
+ }
+
+ // take care of capturing groups
+ var capture = [];
+ for (var i=pattern.indexOf('%'); i>=0; i=pattern.indexOf('%')) {
+ capture.push(pattern[i+1]);
+ pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), '');
+ }
+
+ var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf))
+ // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));
+
+ var initDate = function() {
+ var fixup = function(value, min, max) {
+ return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min);
+ };
+ return {
+ year: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_year', 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999),
+ month: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mon', 'i32', 0, 0, 1) }}}, 0, 11),
+ day: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_mday', 'i32', 0, 0, 1) }}}, 1, 31),
+ hour: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_hour', 'i32', 0, 0, 1) }}}, 0, 23),
+ min: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_min', 'i32', 0, 0, 1) }}}, 0, 59),
+ sec: fixup({{{ makeGetValue('tm', '___tm_struct_layout.tm_sec', 'i32', 0, 0, 1) }}}, 0, 59)
+ };
+ };
+
+ if (matches) {
+ var date = initDate();
+ var value;
+
+ var getMatch = function(symbol) {
+ var pos = capture.indexOf(symbol);
+ // check if symbol appears in regexp
+ if (pos >= 0) {
+ // return matched value or null (falsy!) for non-matches
+ return matches[pos+1];
+ }
+ return;
+ }
+
+ // seconds
+ if ((value=getMatch('S'))) {
+ date.sec = parseInt(value);
+ }
+
+ // minutes
+ if ((value=getMatch('M'))) {
+ date.min = parseInt(value);
+ }
+
+ // hours
+ if ((value=getMatch('H'))) {
+ // 24h clock
+ date.hour = parseInt(value);
+ } else if ((value = getMatch('I'))) {
+ // AM/PM clock
+ var hour = parseInt(value);
+ if ((value=getMatch('p'))) {
+ hour += value.toUpperCase()[0] === 'P' ? 12 : 0;
+ }
+ date.hour = hour;
+ }
+
+ // year
+ if ((value=getMatch('Y'))) {
+ // parse from four-digit year
+ date.year = parseInt(value);
+ } else if ((value=getMatch('y'))) {
+ // parse from two-digit year...
+ var year = parseInt(value);
+ if ((value=getMatch('C'))) {
+ // ...and century
+ year += parseInt(value)*100;
+ } else {
+ // ...and rule-of-thumb
+ year += year<69 ? 2000 : 1900;
+ }
+ date.year = year;
+ }
+
+ // month
+ if ((value=getMatch('m'))) {
+ // parse from month number
+ date.month = parseInt(value)-1;
+ } else if ((value=getMatch('b'))) {
+ // parse from month name
+ date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0;
+ // TODO: derive month from day in year+year, week number+day of week+year
+ }
+
+ // day
+ if ((value=getMatch('d'))) {
+ // get day of month directly
+ date.day = parseInt(value);
+ } else if ((value=getMatch('j'))) {
+ // get day of month from day of year ...
+ var day = parseInt(value);
+ var leapYear = __isLeapYear(date.year);
+ for (var month=0; month<12; ++month) {
+ var daysUntilMonth = __arraySum(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, month-1);
+ if (day<=daysUntilMonth+(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[month]) {
+ date.day = day-daysUntilMonth;
+ }
+ }
+ } else if ((value=getMatch('a'))) {
+ // get day of month from weekday ...
+ var weekDay = value.substring(0,3).toUpperCase();
+ if ((value=getMatch('U'))) {
+ // ... and week number (Sunday being first day of week)
+ // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
+ // All days in a new year preceding the first Sunday are considered to be in week 0.
+ var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay];
+ var weekNumber = parseInt(value);
+
+ // January 1st
+ var janFirst = new Date(date.year, 0, 1);
+ var endDate;
+ if (janFirst.getDay() === 0) {
+ // Jan 1st is a Sunday, and, hence in the 1st CW
+ endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1));
+ } else {
+ // Jan 1st is not a Sunday, and, hence still in the 0th CW
+ endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1));
+ }
+ date.day = endDate.getDate();
+ date.month = endDate.getMonth();
+ } else if ((value=getMatch('W'))) {
+ // ... and week number (Monday being first day of week)
+ // Week number of the year (Monday as the first day of the week) as a decimal number [00,53].
+ // All days in a new year preceding the first Monday are considered to be in week 0.
+ var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay];
+ var weekNumber = parseInt(value);
+
+ // January 1st
+ var janFirst = new Date(date.year, 0, 1);
+ var endDate;
+ if (janFirst.getDay()===1) {
+ // Jan 1st is a Monday, and, hence in the 1st CW
+ endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1));
+ } else {
+ // Jan 1st is not a Monday, and, hence still in the 0th CW
+ endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1));
+ }
+
+ date.day = endDate.getDate();
+ date.month = endDate.getMonth();
+ }
+ }
+
+ /*
+ tm_sec int seconds after the minute 0-61*
+ tm_min int minutes after the hour 0-59
+ tm_hour int hours since midnight 0-23
+ tm_mday int day of the month 1-31
+ tm_mon int months since January 0-11
+ tm_year int years since 1900
+ tm_wday int days since Sunday 0-6
+ tm_yday int days since January 1 0-365
+ tm_isdst int Daylight Saving Time flag
+ */
+
+ var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0);
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_sec', 'fullDate.getSeconds()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_min', 'fullDate.getMinutes()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_hour', 'fullDate.getHours()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_mday', 'fullDate.getDate()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_mon', 'fullDate.getMonth()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_year', 'fullDate.getFullYear()-1900', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_wday', 'fullDate.getDay()', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_yday', '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}
+ {{{ makeSetValue('tm', '___tm_struct_layout.tm_isdst', '0', 'i32') }}}
+
+ // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F
+ // TODO: not sure that intArrayFromString handles all unicode characters correctly
+ return buf+intArrayFromString(matches[0]).length-1;
+ }
+
return 0;
},
strptime_l: 'strptime', // no locale support yet
@@ -6328,15 +6953,15 @@ LibraryManager.library = {
// NOTE: These are fake, since we don't support the C device creation API.
// http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html
makedev: function(maj, min) {
- return 0;
+ return ((maj) << 8 | (min));
},
gnu_dev_makedev: 'makedev',
major: function(dev) {
- return 0;
+ return ((dev) >> 8);
},
gnu_dev_major: 'major',
minor: function(dev) {
- return 0;
+ return ((dev) & 0xff);
},
gnu_dev_minor: 'minor',
diff --git a/src/library_gl.js b/src/library_gl.js
index 959773bc..ee3f8581 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -1076,7 +1076,9 @@ var LibraryGL = {
}
{{{ makeSetValue('count', '0', 'len', 'i32') }}};
for (var i = 0; i < len; ++i) {
- {{{ makeSetValue('shaders', 'i*4', 'GL.shaders[result[i]]', 'i32') }}};
+ var id = GL.shaders.indexOf(result[i]);
+ assert(id !== -1, 'shader not bound to local id');
+ {{{ makeSetValue('shaders', 'i*4', 'id', 'i32') }}};
}
},
diff --git a/src/library_sdl.js b/src/library_sdl.js
index da682eab..7078aa9d 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -940,7 +940,10 @@ var LibrarySDL = {
// TODO
},
- SDL_GetKeyboardState: function() {
+ SDL_GetKeyboardState: function(numKeys) {
+ if (numKeys) {
+ {{{ makeSetValue('numKeys', 0, 0x10000, 'i32') }}};
+ }
return SDL.keyboardState;
},
@@ -1097,7 +1100,11 @@ var LibrarySDL = {
return ret;
},
+ rotozoomSurface__deps: ['zoomSurface'],
rotozoomSurface: function(src, angle, zoom, smooth) {
+ if (angle % 360 === 0) {
+ return _zoomSurface(src, zoom, zoom, smooth);
+ }
var srcData = SDL.surfaces[src];
var w = srcData.width * zoom;
var h = srcData.height * zoom;
@@ -1828,6 +1835,11 @@ var LibrarySDL = {
return Math.floor(fontData.size*0.02); // XXX
},
+ TTF_FontHeight: function(font) {
+ var fontData = SDL.fonts[font];
+ return fontData.size;
+ },
+
// SDL gfx
$SDL_gfx: {
diff --git a/src/modules.js b/src/modules.js
index a6aa2644..9f419234 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -252,12 +252,26 @@ var Functions = {
for (var i = 0; i < argTypes.length; i++) {
var type = argTypes[i];
if (!type) break; // varargs
- sig += isIntImplemented(type) ? (getBits(type) == 64 ? 'ii' : 'i') : 'f'; // legalized i64s will be i32s
+ if (type in Runtime.FLOAT_TYPES) {
+ sig += 'f';
+ } else {
+ var chunks = getNumIntChunks(type);
+ for (var j = 0; j < chunks; j++) sig += 'i';
+ }
}
if (hasVarArgs) sig += 'i';
return sig;
},
+ getSignatureReturnType: function(sig) {
+ switch(sig[0]) {
+ case 'v': return 'void';
+ case 'i': return 'i32';
+ case 'f': return 'double';
+ default: throw 'what is this sig? ' + sig;
+ }
+ },
+
// Mark a function as needing indexing. Python will coordinate them all
getIndex: function(ident, doNotCreate, sig) {
if (doNotCreate && !(ident in this.indexedFunctions)) {
@@ -449,7 +463,8 @@ var PassManager = {
Types: Types,
Variables: Variables,
Functions: Functions,
- EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS // needed for asm.js global constructors (ctors)
+ EXPORTED_FUNCTIONS: EXPORTED_FUNCTIONS, // needed for asm.js global constructors (ctors)
+ Runtime: { GLOBAL_BASE: Runtime.GLOBAL_BASE }
}));
} else if (phase == 'funcs') {
print('\n//FORWARDED_DATA:' + JSON.stringify({
diff --git a/src/parseTools.js b/src/parseTools.js
index eb200c65..72166592 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -969,6 +969,12 @@ function generateStructTypes(type) {
}
ret[index++] = type;
} else {
+ if (Runtime.isStructType(type) && type[1] === '0') {
+ // this is [0 x something]. When inside another structure like here, it must be at the end,
+ // and it does nothing
+ assert(i === typeData.fields.length-1);
+ return;
+ }
add(Types.types[type]);
}
var more = (i+1 < typeData.fields.length ? typeData.flatIndexes[i+1] : typeData.flatSize) - (index - start);
@@ -1231,7 +1237,7 @@ function indexizeFunctions(value, type) {
// add signature to library functions that we now know need indexing
var sig = Functions.implementedFunctions[value] || Functions.unimplementedFunctions[value];
if (!sig) {
- sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : []);
+ sig = Functions.unimplementedFunctions[value] = Functions.getSignature(out.returnType, out.segments ? out.segments.map(function(segment) { return segment[0].text }) : [], isVarArgsFunctionType(type));
}
return Functions.getIndex(value, undefined, sig);
}
@@ -1694,7 +1700,7 @@ function makeGetSlabs(ptr, type, allowMultiple, unsigned) {
}
case 'float': return ['HEAPF32'];
default: {
- throw 'what, exactly, can we do for unknown types in TA2?! ' + new Error().stack;
+ throw 'what, exactly, can we do for unknown types in TA2?! ' + [new Error().stack, ptr, type, allowMultiple, unsigned];
}
}
}
diff --git a/src/preamble.js b/src/preamble.js
index c9e7e4eb..411abce6 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -796,6 +796,7 @@ Math['imul'] = function(a, b) {
return (a*b)|0; // fast but imprecise
};
#endif
+Math.imul = Math['imul'];
// A counter of dependencies for calling run(). If we need to
// do asynchronous work before running, increment this and
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index ca9c6ab1..a457914b 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -180,7 +180,7 @@ void Block::Render(bool InLoop) {
Block *DefaultTarget(NULL); // The block we branch to without checking the condition, if none of the other conditions held.
- // We must do this here, because blocks can be split and even comparing their Ids is not enough. We must check the conditions.
+ // Find the default target, the one without a condition
for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) {
if (!iter->second->Condition) {
assert(!DefaultTarget); // Must be exactly one default
diff --git a/src/runtime.js b/src/runtime.js
index 684f11e7..8c2c8f4d 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -201,14 +201,24 @@ var Runtime = {
type.alignSize = 0;
var diffs = [];
var prev = -1;
+ var index = 0;
type.flatIndexes = type.fields.map(function(field) {
+ index++;
var size, alignSize;
if (Runtime.isNumberType(field) || Runtime.isPointerType(field)) {
size = Runtime.getNativeTypeSize(field); // pack char; char; in structs, also char[X]s.
alignSize = Runtime.getAlignSize(field, size);
} else if (Runtime.isStructType(field)) {
- size = Types.types[field].flatSize;
- alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize);
+ if (field[1] === '0') {
+ // this is [0 x something]. When inside another structure like here, it must be at the end,
+ // and it adds no size
+ assert(index === type.fields.length);
+ size = 0;
+ alignSize = type.alignSize || QUANTUM_SIZE;
+ } else {
+ size = Types.types[field].flatSize;
+ alignSize = Runtime.getAlignSize(null, Types.types[field].alignSize);
+ }
} else if (field[0] == 'b') {
// bN, large number field, like a [N x i8]
size = field.substr(1)|0;
diff --git a/src/settings.js b/src/settings.js
index 7f9dca3b..b7460cf2 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -125,6 +125,11 @@ var INLINING_LIMIT = 0; // A limit on inlining. If 0, we will inline normally i
// we will prevent inlining of functions of this size or larger
// in closure. 50 is a reasonable setting if you do not want
// inlining
+var OUTLINING_LIMIT = 0; // A function size above which we try to automatically break up
+ // functions into smaller ones, to avoid the downsides of very
+ // large functions (JS engines often compile them very slowly,
+ // compile them with lower optimizations, or do not optimize them
+ // at all). If 0, we do not perform outlining at all.
// Generated code debugging options
var SAFE_HEAP = 0; // Check each write to the heap, for example, this will give a clear
@@ -238,6 +243,8 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r
// a new project and want to see a list of file system operations happening
// so that you can create a virtual file system with all of the required files.
+var USE_OLD_FS = 1; // Switch to toggle the new / old FS code. Currently only used for testing purposes.
+
var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss
// When enabled, 0-initialized globals are sorted to the end of the globals list,
// enabling us to not explicitly store the initialization value for each 0 byte.
@@ -327,6 +334,10 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as
// LINKABLE of 0 is very useful in that we can reduce the size of the
// generated code very significantly, by removing everything not actually used.
+var DLOPEN_SUPPORT = 0; // Whether to support dlopen(NULL, ...) which enables dynamic access to the
+ // module's functions and globals. Implies LINKABLE=1, because we do not want
+ // dead code elimination.
+
var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This
// increases the size of the generated script, but allows you
// to more easily perform operations from handwritten JS on
@@ -403,7 +414,6 @@ var DEBUG_TAGS_SHOWING = [];
// metadata
// legalizer
-
// A cached set of defines, generated from the header files. This
// lets the emscripten libc (library.js) see the right values.
// If you the headers or use different ones, you will need to override
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index cd465e45..403d8084 100644
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -70,13 +70,13 @@ namespace emscripten {
GenericFunction invoker,
GenericFunction function);
- void _embind_register_tuple(
+ void _embind_register_value_array(
TYPEID tupleType,
const char* name,
GenericFunction constructor,
GenericFunction destructor);
- void _embind_register_tuple_element(
+ void _embind_register_value_array_element(
TYPEID tupleType,
TYPEID getterReturnType,
GenericFunction getter,
@@ -85,15 +85,15 @@ namespace emscripten {
GenericFunction setter,
void* setterContext);
- void _embind_finalize_tuple(TYPEID tupleType);
+ void _embind_finalize_value_array(TYPEID tupleType);
- void _embind_register_struct(
+ void _embind_register_value_object(
TYPEID structType,
const char* fieldName,
GenericFunction constructor,
GenericFunction destructor);
- void _embind_register_struct_field(
+ void _embind_register_value_object_field(
TYPEID structType,
const char* fieldName,
TYPEID getterReturnType,
@@ -103,7 +103,7 @@ namespace emscripten {
GenericFunction setter,
void* setterContext);
- void _embind_finalize_struct(TYPEID structType);
+ void _embind_finalize_value_object(TYPEID structType);
void _embind_register_smart_ptr(
TYPEID pointerType,
@@ -531,26 +531,26 @@ namespace emscripten {
////////////////////////////////////////////////////////////////////////////////
template<typename ClassType>
- class value_tuple : public internal::noncopyable {
+ class value_array : public internal::noncopyable {
public:
- value_tuple(const char* name) {
+ value_array(const char* name) {
using namespace internal;
- _embind_register_tuple(
+ _embind_register_value_array(
TypeID<ClassType>::get(),
name,
reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>),
reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>));
}
- ~value_tuple() {
+ ~value_array() {
using namespace internal;
- _embind_finalize_tuple(TypeID<ClassType>::get());
+ _embind_finalize_value_array(TypeID<ClassType>::get());
}
template<typename InstanceType, typename ElementType>
- value_tuple& element(ElementType InstanceType::*field) {
+ value_array& element(ElementType InstanceType::*field) {
using namespace internal;
- _embind_register_tuple_element(
+ _embind_register_value_array_element(
TypeID<ClassType>::get(),
TypeID<ElementType>::get(),
reinterpret_cast<GenericFunction>(
@@ -566,11 +566,11 @@ namespace emscripten {
}
template<typename Getter, typename Setter>
- value_tuple& element(Getter getter, Setter setter) {
+ value_array& element(Getter getter, Setter setter) {
using namespace internal;
typedef GetterPolicy<Getter> GP;
typedef SetterPolicy<Setter> SP;
- _embind_register_tuple_element(
+ _embind_register_value_array_element(
TypeID<ClassType>::get(),
TypeID<typename GP::ReturnType>::get(),
reinterpret_cast<GenericFunction>(&GP::template get<ClassType>),
@@ -582,11 +582,11 @@ namespace emscripten {
}
template<int Index>
- value_tuple& element(index<Index>) {
+ value_array& element(index<Index>) {
using namespace internal;
ClassType* null = 0;
typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
- _embind_register_tuple_element(
+ _embind_register_value_array_element(
TypeID<ClassType>::get(),
TypeID<ElementType>::get(),
reinterpret_cast<GenericFunction>(&internal::get_by_index<ClassType, ElementType>),
@@ -603,25 +603,25 @@ namespace emscripten {
////////////////////////////////////////////////////////////////////////////////
template<typename ClassType>
- class value_struct : public internal::noncopyable {
+ class value_object : public internal::noncopyable {
public:
- value_struct(const char* name) {
+ value_object(const char* name) {
using namespace internal;
- _embind_register_struct(
+ _embind_register_value_object(
TypeID<ClassType>::get(),
name,
reinterpret_cast<GenericFunction>(&raw_constructor<ClassType>),
reinterpret_cast<GenericFunction>(&raw_destructor<ClassType>));
}
- ~value_struct() {
- _embind_finalize_struct(internal::TypeID<ClassType>::get());
+ ~value_object() {
+ _embind_finalize_value_object(internal::TypeID<ClassType>::get());
}
template<typename InstanceType, typename FieldType>
- value_struct& field(const char* fieldName, FieldType InstanceType::*field) {
+ value_object& field(const char* fieldName, FieldType InstanceType::*field) {
using namespace internal;
- _embind_register_struct_field(
+ _embind_register_value_object_field(
TypeID<ClassType>::get(),
fieldName,
TypeID<FieldType>::get(),
@@ -638,7 +638,7 @@ namespace emscripten {
}
template<typename Getter, typename Setter>
- value_struct& field(
+ value_object& field(
const char* fieldName,
Getter getter,
Setter setter
@@ -646,7 +646,7 @@ namespace emscripten {
using namespace internal;
typedef GetterPolicy<Getter> GP;
typedef SetterPolicy<Setter> SP;
- _embind_register_struct_field(
+ _embind_register_value_object_field(
TypeID<ClassType>::get(),
fieldName,
TypeID<typename GP::ReturnType>::get(),
@@ -659,11 +659,11 @@ namespace emscripten {
}
template<int Index>
- value_struct& field(const char* fieldName, index<Index>) {
+ value_object& field(const char* fieldName, index<Index>) {
using namespace internal;
ClassType* null = 0;
typedef typename std::remove_reference<decltype((*null)[Index])>::type ElementType;
- _embind_register_struct_field(
+ _embind_register_value_object_field(
TypeID<ClassType>::get(),
fieldName,
TypeID<ElementType>::get(),
diff --git a/system/include/libc/sys/stat.h b/system/include/libc/sys/stat.h
index e2b20187..2285b294 100644
--- a/system/include/libc/sys/stat.h
+++ b/system/include/libc/sys/stat.h
@@ -179,7 +179,8 @@ struct stat64
#endif
int _EXFUN(chmod,( const char *__path, mode_t __mode ));
-int _EXFUN(fchmod,(int __fd, mode_t __mode));
+int _EXFUN(lchmod,( const char *__path, mode_t __mode ));
+int _EXFUN(fchmod,(int __fd, mode_t __mode));
int _EXFUN(fstat,( int __fd, struct stat *__sbuf ));
int _EXFUN(fstat64,( int __fd, struct stat64 *__sbuf )); /* XXX Emscripten */
int _EXFUN(mkdir,( const char *_path, mode_t __mode ));
diff --git a/system/include/libc/sys/types.h b/system/include/libc/sys/types.h
index c36f724c..fe5d552a 100644
--- a/system/include/libc/sys/types.h
+++ b/system/include/libc/sys/types.h
@@ -159,6 +159,10 @@ typedef __gid_t gid_t;
typedef __id_t id_t ; /* can hold a uid_t or pid_t */
#endif
+__int32_t major(__uint32_t _x);
+__int32_t minor(__uint32_t _x);
+dev_t makedev(__uint32_t _major, __uint32_t _minor);
+
#if defined(__XMK__)
typedef signed char pid_t;
#else
diff --git a/system/include/net/netinet/in.h b/system/include/net/netinet/in.h
index cf324f3d..fba1a7b3 100644
--- a/system/include/net/netinet/in.h
+++ b/system/include/net/netinet/in.h
@@ -55,6 +55,13 @@ extern const struct in6_addr in6addr_interfacelocal_allnodes;
extern const struct in6_addr in6addr_interfacelocal_allrouters;
extern const struct in6_addr in6addr_sitelocal_allrouters;
+#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
+#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \
+ { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
+#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
+ { { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } }
+
struct sockaddr_in6 {
int sin6_family;
unsigned short sin6_port;
diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h
index 3168f85b..9650bb9a 100644
--- a/system/include/sys/socket.h
+++ b/system/include/sys/socket.h
@@ -34,7 +34,8 @@ extern "C" {
#define SO_LINGER 130
#define SO_BSDCOMPAT 140
-#define SHUT_RD 1
+#define SHUT_RD 0
+#define SHUT_WR 1
#define SHUT_RDWR 2
typedef unsigned int sa_family_t;
diff --git a/tests/cases/entry3.ll b/tests/cases/entry3.ll
new file mode 100644
index 00000000..a20c6843
--- /dev/null
+++ b/tests/cases/entry3.ll
@@ -0,0 +1,36 @@
+; ModuleID = '/tmp/tmpKnA2D3/a.out.bc'
+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 [11 x i8] c"getgid=%d\0A\00", align 1
+@.str1 = private unnamed_addr constant [6 x i8] c"f=%d\0A\00", align 1
+
+define internal i32 @_Z1fii(i32, i32) noinline {
+entry:
+ %3 = tail call i32 @getgid()
+ %4 = icmp eq i32 %3, 0
+ br i1 %4, label %cond.b, label %cond.a
+
+cond.a:
+ %6 = tail call i32 @getgid()
+ br label %cond.end
+
+cond.b:
+ br label %cond.end
+
+cond.end:
+ %.0 = phi i32 [ 0, %cond.b ], [ 1, %1 ]
+ ret i32 %.0
+}
+
+declare i32 @getgid()
+
+define i32 @main() {
+ %1 = tail call i32 @getgid()
+ %2 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i32 %1)
+ %3 = tail call i32 @_Z1fii(i32 undef, i32 undef)
+ %4 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0), i32 %3)
+ ret i32 0
+}
+
+declare i32 @printf(i8* nocapture, ...) nounwind
diff --git a/tests/cases/entry3.txt b/tests/cases/entry3.txt
new file mode 100644
index 00000000..4060fb06
--- /dev/null
+++ b/tests/cases/entry3.txt
@@ -0,0 +1,2 @@
+getgid=0
+f=0
diff --git a/tests/cases/i32_mem.ll b/tests/cases/i32_mem.ll
new file mode 100644
index 00000000..e50014ca
--- /dev/null
+++ b/tests/cases/i32_mem.ll
@@ -0,0 +1,23 @@
+; ModuleID = 'tests/hello_world.bc'
+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".%x.\0A\00", align 1 ; [#uses=1 type=[5 x i8]*]
+
+define i32 @main() {
+entry:
+ %mem = alloca i32
+ store i32 4279383126, i32* %mem
+ %i24 = bitcast i32* %mem to i24*
+ %load = load i24* %i24, align 4
+ %load32 = zext i24 %load to i32
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32)
+ %val_24 = trunc i32 4041265344 to i24
+ store i24 %val_24, i24* %i24, align 4
+ %load32b = load i32* %mem, align 4
+ %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([5 x i8]* @.str, i32 0, i32 0), i32 %load32b)
+ ret i32 1
+}
+
+; [#uses=1]
+declare i32 @printf(i8*, ...)
diff --git a/tests/cases/i32_mem.txt b/tests/cases/i32_mem.txt
new file mode 100644
index 00000000..683e58e2
--- /dev/null
+++ b/tests/cases/i32_mem.txt
@@ -0,0 +1,2 @@
+.123456.
+.ffe0d0c0.
diff --git a/tests/cases/sillyfuncast.ll b/tests/cases/sillyfuncast.ll
new file mode 100644
index 00000000..36c26720
--- /dev/null
+++ b/tests/cases/sillyfuncast.ll
@@ -0,0 +1,23 @@
+; ModuleID = 'tests/hello_world.bc'
+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!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define void @doit() {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ store i32 0, i32* %retval
+ %58 = tail call i32 bitcast (void ()* @doit to i32 ()*)() nounwind
+ ret i32 1
+}
+
+declare i32 @printf(i8*, ...)
+
+
+
diff --git a/tests/cases/sillyfuncast2.ll b/tests/cases/sillyfuncast2.ll
new file mode 100644
index 00000000..f72ebe28
--- /dev/null
+++ b/tests/cases/sillyfuncast2.ll
@@ -0,0 +1,21 @@
+; ModuleID = 'tests/hello_world.bc'
+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!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*]
+
+define void @doit(i32 %one, i32 %two) {
+ %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32]
+ ret void
+}
+
+define i32 @main() {
+entry:
+ %retval = alloca i32, align 4 ; [#uses=1 type=i32*]
+ store i32 0, i32* %retval
+ call i32 bitcast (void (i32, i32)* @doit to i32 (i32, i64)*)(i32 0, i64 0) nounwind
+ ret i32 1
+}
+
+declare i32 @printf(i8*, ...)
+
diff --git a/tests/cases/zeroembedded.ll b/tests/cases/zeroembedded.ll
new file mode 100644
index 00000000..6a4f6073
--- /dev/null
+++ b/tests/cases/zeroembedded.ll
@@ -0,0 +1,23 @@
+; a.ll
+%struct.pypy_str = type { i32, [0 x i8] }
+%struct.pypy_strval = type { i32, [13 x i8] }
+
+%union.pypy_array3_len0u = type { %struct.pypy_array3_len0 }
+%struct.pypy_array3_len0 = type { i32, i32, [0 x i8] }
+
+@pypy_g_strval = global %struct.pypy_strval { i32 13, [13 x i8] c"hello world\0A\00" }
+@pypy_g_strval2 = global %struct.pypy_array3_len0 { i32 13, i32 111, [0 x i8] c"" }
+
+declare i32 @printf(i8*, ...)
+
+define i32 @main(i32 %argc, i8** nocapture %argv) {
+ %waka = alloca %struct.pypy_array3_len0
+ %1 = bitcast %struct.pypy_strval* @pypy_g_strval to %struct.pypy_str*
+ %2 = getelementptr inbounds %struct.pypy_str* %1, i32 1
+ %3 = bitcast %struct.pypy_str* %2 to i8*
+ call i32 (i8*, ...)* @printf(i8* %3)
+ %unneeded = bitcast %struct.pypy_str* %2 to %struct.pypy_array3_len0*
+ call i32 (i8*, ...)* @printf(i8* %3, %struct.pypy_array3_len0* %unneeded)
+ ret i32 0
+}
+
diff --git a/tests/cases/zeroembedded.txt b/tests/cases/zeroembedded.txt
new file mode 100644
index 00000000..3b18e512
--- /dev/null
+++ b/tests/cases/zeroembedded.txt
@@ -0,0 +1 @@
+hello world
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index e60e1ab3..da81a81e 100644
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -898,10 +898,7 @@ module({
test("can clone handles", function() {
var a = cm.emval_test_get_function_ptr();
- assert.equal(1, a.$$.count.value);
var b = a.clone();
- assert.equal(2, a.$$.count.value);
- assert.equal(2, b.$$.count.value);
a.delete();
assert.throws(cm.BindingError, function() {
@@ -1149,7 +1146,7 @@ module({
a.set(b);
var c = a.get();
- assert.equal(b.$$.ptr, c.$$.ptr);
+ assert.true(b.isAliasOf(c));
b.delete();
c.delete();
a.delete();
@@ -1747,8 +1744,8 @@ module({
BaseFixture.extend("constants", function() {
assert.equal(10, cm.INT_CONSTANT);
assert.equal("some string", cm.STRING_CONSTANT);
- assert.deepEqual([1, 2, 3, 4], cm.VALUE_TUPLE_CONSTANT);
- assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_STRUCT_CONSTANT);
+ assert.deepEqual([1, 2, 3, 4], cm.VALUE_ARRAY_CONSTANT);
+ assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_OBJECT_CONSTANT);
});
BaseFixture.extend("object handle comparison", function() {
@@ -1881,6 +1878,16 @@ module({
// setTimeout(fn, 0);
// });
});
+
+ BaseFixture.extend("references", function() {
+ test("JS object handles can be passed through to C++ by reference", function() {
+ var sh = new cm.StringHolder("Hello world");
+ assert.equal("Hello world", sh.get());
+ cm.clear_StringHolder(sh);
+ assert.equal("", sh.get());
+ sh.delete();
+ });
+ });
});
/* global run_all_tests */
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index 3561b8a1..d6b27bce 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1445,7 +1445,7 @@ EMSCRIPTEN_BINDINGS(tests) {
//function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct);
- value_tuple<TupleVector>("TupleVector")
+ value_array<TupleVector>("TupleVector")
.element(&TupleVector::x)
.element(&Vector::getY, &Vector::setY)
.element(&readVectorZ, &writeVectorZ)
@@ -1455,13 +1455,13 @@ EMSCRIPTEN_BINDINGS(tests) {
function("emval_test_return_TupleVector", &emval_test_return_TupleVector);
function("emval_test_take_and_return_TupleVector", &emval_test_take_and_return_TupleVector);
- value_tuple<TupleVectorTuple>("TupleVectorTuple")
+ value_array<TupleVectorTuple>("TupleVectorTuple")
.element(&TupleVectorTuple::v)
;
function("emval_test_return_TupleVectorTuple", &emval_test_return_TupleVectorTuple);
- value_struct<StructVector>("StructVector")
+ value_object<StructVector>("StructVector")
.field("x", &StructVector::x)
.field("y", &Vector::getY, &Vector::setY)
.field("z", &readVectorZ, &writeVectorZ)
@@ -1471,7 +1471,7 @@ EMSCRIPTEN_BINDINGS(tests) {
function("emval_test_return_StructVector", &emval_test_return_StructVector);
function("emval_test_take_and_return_StructVector", &emval_test_take_and_return_StructVector);
- value_struct<TupleInStruct>("TupleInStruct")
+ value_object<TupleInStruct>("TupleInStruct")
.field("field", &TupleInStruct::field)
;
@@ -2077,12 +2077,12 @@ OrderedStruct getOrderedStruct() {
}
EMSCRIPTEN_BINDINGS(order) {
- value_tuple<OrderedTuple>("OrderedTuple")
+ value_array<OrderedTuple>("OrderedTuple")
.element(&OrderedTuple::first)
.element(&OrderedTuple::second)
;
- value_struct<OrderedStruct>("OrderedStruct")
+ value_object<OrderedStruct>("OrderedStruct")
.field("first", &OrderedStruct::first)
.field("second", &OrderedStruct::second)
;
@@ -2215,10 +2215,10 @@ EMSCRIPTEN_BINDINGS(constants) {
constant("STRING_CONSTANT", std::string("some string"));
TupleVector tv(1, 2, 3, 4);
- constant("VALUE_TUPLE_CONSTANT", tv);
+ constant("VALUE_ARRAY_CONSTANT", tv);
StructVector sv(1, 2, 3, 4);
- constant("VALUE_STRUCT_CONSTANT", sv);
+ constant("VALUE_OBJECT_CONSTANT", sv);
}
class DerivedWithOffset : public DummyDataToTestPointerAdjustment, public Base {
@@ -2235,3 +2235,11 @@ EMSCRIPTEN_BINDINGS(with_adjustment) {
function("return_Base_from_DerivedWithOffset", &return_Base_from_DerivedWithOffset);
}
+
+void clear_StringHolder(StringHolder& sh) {
+ sh.set("");
+}
+
+EMSCRIPTEN_BINDINGS(references) {
+ function("clear_StringHolder", &clear_StringHolder);
+}
diff --git a/tests/glgetattachedshaders.c b/tests/glgetattachedshaders.c
new file mode 100644
index 00000000..303e0f92
--- /dev/null
+++ b/tests/glgetattachedshaders.c
@@ -0,0 +1,93 @@
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void die(const char *msg)
+{
+ printf("%s\n", msg);
+ abort();
+}
+
+static void create_context(void)
+{
+ EGLint num_config;
+ EGLContext g_egl_ctx;
+ EGLDisplay g_egl_dpy;
+ EGLConfig g_config;
+
+ static const EGLint attribute_list[] =
+ {
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE
+ };
+
+ static const EGLint context_attributes[] =
+ {
+ EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL_NONE
+ };
+
+ g_egl_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!g_egl_dpy)
+ die("failed to create display");
+
+ if (!eglInitialize(g_egl_dpy, NULL, NULL))
+ die("failed to initialize egl");
+
+ if (!eglChooseConfig(g_egl_dpy, attribute_list, &g_config, 1, &num_config))
+ die("failed to choose config");
+
+ g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, context_attributes);
+ if (!g_egl_ctx)
+ die("failed to create context");
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned i;
+
+ create_context();
+
+ GLuint prog = glCreateProgram();
+ if (glGetError())
+ die("failed to create program");
+
+ GLuint vertex = glCreateShader(GL_VERTEX_SHADER);
+ if (glGetError())
+ die("failed to create vertex shader");
+ glAttachShader(prog, vertex);
+
+ GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER);
+ if (glGetError())
+ die("failed to create fragment shader");
+ glAttachShader(prog, fragment);
+
+ GLuint shaders[2];
+ GLsizei count;
+
+ glGetAttachedShaders(prog, 2, &count, shaders);
+ if (glGetError())
+ die("failed to get attached shaders");
+ if (count != 2)
+ die("unknown number of shaders returned");
+ if (shaders[0] == shaders[1])
+ die("returned identical shaders");
+
+ for (i = 0; i < count; i++)
+ {
+ if (shaders[i] == 0)
+ die("returned 0");
+ if (shaders[i] != vertex && shaders[i] != fragment)
+ die("unknown shader returned");
+ }
+
+ int result = 1;
+ REPORT_RESULT();
+
+ return 0;
+}
diff --git a/tests/runner.py b/tests/runner.py
index 06d3a190..4a9bf623 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -27,6 +27,7 @@ Running the main part of the test suite. Don't forget to run the other parts!
benchmark - run before and after each set of changes before pushing to
master, verify no regressions
browser - runs pages in a web browser
+ browser audio - runs audio tests in a web browser (requires human verification)
To run one of those parts, do something like
@@ -270,6 +271,8 @@ process(sys.argv[1])
print >> sys.stderr, "[was asm.js'ified]"
elif 'asm.js' in err: # if no asm.js error, then not an odin build
raise Exception("did NOT asm.js'ify")
+ err = '\n'.join(filter(lambda line: 'successfully compiled asm.js code' not in line, err.split('\n')))
+ return err
def run_generated_code(self, engine, filename, args=[], check_timeout=True, output_nicerizer=None):
stdout = os.path.join(self.get_dir(), 'stdout') # use files, as PIPE can get too full and hang us
@@ -285,7 +288,7 @@ process(sys.argv[1])
out = open(stdout, 'r').read()
err = open(stderr, 'r').read()
if engine == SPIDERMONKEY_ENGINE and Settings.ASM_JS:
- self.validate_asmjs(err)
+ err = self.validate_asmjs(err)
if output_nicerizer:
ret = output_nicerizer(out, err)
else:
@@ -1981,6 +1984,18 @@ Succeeded!
self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 "
"1 2 -1 -2 2 2 -2 -2")
+ def test_llrint(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+ src = r'''
+ #include <stdio.h>
+ #include <math.h>
+ int main() {
+ printf("%lld\n%lld\n%lld\n%lld\n", llrint(0.1), llrint(0.6), llrint(1.25), llrint(1099511627776.667));
+ return 0;
+ }
+ '''
+ self.do_run(src, '0\n1\n1\n1099511627777\n')
+
def test_getgep(self):
# Generated code includes getelementptr (getelementptr, 0, 1), i.e., GEP as the first param to GEP
src = '''
@@ -2954,6 +2969,37 @@ back
self.emcc_args.pop() ; self.emcc_args.pop() # disable closure to work around a closure bug
self.do_run(src, 'Throw...Construct...Catched...Destruct...Throw...Construct...Copy...Catched...Destruct...Destruct...')
+ def test_exception_2(self):
+ if self.emcc_args is None: return self.skip('need emcc to add in libcxx properly')
+ Settings.DISABLE_EXCEPTION_CATCHING = 0
+ src = r'''
+ #include <stdexcept>
+ #include <stdio.h>
+
+ typedef void (*FuncPtr)();
+
+ void ThrowException()
+ {
+ throw std::runtime_error("catch me!");
+ }
+
+ FuncPtr ptr = ThrowException;
+
+ int main()
+ {
+ try
+ {
+ ptr();
+ }
+ catch(...)
+ {
+ printf("Exception caught successfully!\n");
+ }
+ return 0;
+ }
+ '''
+ self.do_run(src, 'Exception caught successfully!')
+
def test_white_list_exception(self):
Settings.DISABLE_EXCEPTION_CATCHING = 2
Settings.EXCEPTION_CATCHING_WHITELIST = ["__Z12somefunctionv"]
@@ -4509,6 +4555,22 @@ The current address of a is: 0x12345678
The current type of b is: 9
''')
+ def test_functionpointer_libfunc_varargs(self):
+ src = r'''
+ #include <stdio.h>
+ #include <fcntl.h>
+ typedef int (*fp_t)(int, int, ...);
+ int main(int argc, char **argv) {
+ fp_t fp = &fcntl;
+ if (argc == 1337) fp = (fp_t)&main;
+ (*fp)(0, 10);
+ (*fp)(0, 10, 5);
+ printf("waka\n");
+ return 0;
+ }
+ '''
+ self.do_run(src, '''waka''')
+
def test_structbyval(self):
Settings.INLINING_LIMIT = 50
@@ -4958,6 +5020,286 @@ The current type of b is: 9
'''
self.do_run(src, 'time: ') # compilation check, mainly
+
+ def test_strptime_tm(self):
+ src=r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ int main() {
+ struct tm tm;
+ char *ptr = strptime("17410105012000", "%H%M%S%d%m%Y", &tm);
+
+ printf("%s: %s, %d/%d/%d %d:%d:%d",
+ (ptr != NULL && *ptr=='\0') ? "OK" : "ERR",
+ tm.tm_wday == 0 ? "Sun" : (tm.tm_wday == 1 ? "Mon" : (tm.tm_wday == 2 ? "Tue" : (tm.tm_wday == 3 ? "Wed" : (tm.tm_wday == 4 ? "Thu" : (tm.tm_wday == 5 ? "Fri" : (tm.tm_wday == 6 ? "Sat" : "ERR")))))),
+ tm.tm_mon+1,
+ tm.tm_mday,
+ tm.tm_year+1900,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec
+ );
+ }
+ '''
+ self.do_run(src, 'OK: Wed, 1/5/2000 17:41:1')
+
+ def test_strptime_days(self):
+ src = r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ static const struct {
+ const char *input;
+ const char *format;
+ } day_tests[] = {
+ { "2000-01-01", "%Y-%m-%d"},
+ { "03/03/00", "%D"},
+ { "9/9/99", "%x"},
+ { "19990502123412", "%Y%m%d%H%M%S"},
+ { "2001 20 Mon", "%Y %U %a"},
+ { "2006 4 Fri", "%Y %U %a"},
+ { "2001 21 Mon", "%Y %W %a"},
+ { "2013 29 Wed", "%Y %W %a"},
+ { "2000-01-01 08:12:21 AM", "%Y-%m-%d %I:%M:%S %p"},
+ { "2000-01-01 08:12:21 PM", "%Y-%m-%d %I:%M:%S %p"},
+ { "2001 17 Tue", "%Y %U %a"},
+ { "2001 8 Thursday", "%Y %W %a"},
+ };
+
+ int main() {
+ struct tm tm;
+
+ for (int i = 0; i < sizeof (day_tests) / sizeof (day_tests[0]); ++i) {
+ memset (&tm, '\0', sizeof (tm));
+ char *ptr = strptime(day_tests[i].input, day_tests[i].format, &tm);
+
+ printf("%s: %d/%d/%d (%dth DoW, %dth DoY)\n", (ptr != NULL && *ptr=='\0') ? "OK" : "ERR", tm.tm_mon+1, tm.tm_mday, 1900+tm.tm_year, tm.tm_wday, tm.tm_yday);
+ }
+ }
+ '''
+ self.do_run(src, 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 3/3/2000 (5th DoW, 62th DoY)\n'\
+ 'OK: 9/9/1999 (4th DoW, 251th DoY)\n'\
+ 'OK: 5/2/1999 (0th DoW, 121th DoY)\n'\
+ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\
+ 'OK: 1/27/2006 (5th DoW, 26th DoY)\n'\
+ 'OK: 5/21/2001 (1th DoW, 140th DoY)\n'\
+ 'OK: 7/24/2013 (3th DoW, 204th DoY)\n'\
+ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 1/1/2000 (6th DoW, 0th DoY)\n'\
+ 'OK: 5/1/2001 (2th DoW, 120th DoY)\n'\
+ 'OK: 2/22/2001 (4th DoW, 52th DoY)\n'\
+ )
+
+ def test_strptime_reentrant(self):
+ src=r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ int main () {
+ int result = 0;
+ struct tm tm;
+
+ memset (&tm, 0xaa, sizeof (tm));
+
+ /* Test we don't crash on uninitialized struct tm.
+ Some fields might contain bogus values until everything
+ needed is initialized, but we shouldn't crash. */
+ if (strptime ("2007", "%Y", &tm) == NULL
+ || strptime ("12", "%d", &tm) == NULL
+ || strptime ("Feb", "%b", &tm) == NULL
+ || strptime ("13", "%M", &tm) == NULL
+ || strptime ("21", "%S", &tm) == NULL
+ || strptime ("16", "%H", &tm) == NULL) {
+ printf("ERR: returned NULL");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16
+ || tm.tm_mday != 12 || tm.tm_mon != 1 || tm.tm_year != 107
+ || tm.tm_wday != 1 || tm.tm_yday != 42) {
+ printf("ERR: unexpected tm content (1) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strptime ("8", "%d", &tm) == NULL) {
+ printf("ERR: strptime failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (tm.tm_sec != 21 || tm.tm_min != 13 || tm.tm_hour != 16
+ || tm.tm_mday != 8 || tm.tm_mon != 1 || tm.tm_year != 107
+ || tm.tm_wday != 4 || tm.tm_yday != 38) {
+ printf("ERR: unexpected tm content (2) - %d/%d/%d %d:%d:%d", tm.tm_mon+1, tm.tm_mday, tm.tm_year+1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("OK");
+ }
+ '''
+ self.do_run(src, 'OK')
+
+ def test_strftime(self):
+ src=r'''
+ #include <time.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdlib.h>
+
+ void test(int result, const char* comment, const char* parsed = "") {
+ printf("%d",result);
+ if (!result) {
+ printf("\nERROR: %s (\"%s\")\n", comment, parsed);
+ }
+ }
+
+ int cmp(const char *s1, const char *s2) {
+ for ( ; *s1 == *s2 ; s1++,s2++ ) {
+ if ( *s1 == '\0' )
+ break;
+ }
+
+ return (*s1 - *s2);
+ }
+
+ int main() {
+ struct tm tm;
+ char s[1000];
+ size_t size;
+
+ tm.tm_sec = 4;
+ tm.tm_min = 23;
+ tm.tm_hour = 20;
+ tm.tm_mday = 21;
+ tm.tm_mon = 1;
+ tm.tm_year = 74;
+ tm.tm_wday = 4;
+ tm.tm_yday = 51;
+ tm.tm_isdst = 0;
+
+ size = strftime(s, 1000, "", &tm);
+ test((size==0) && (*s=='\0'), "strftime test #1", s);
+
+ size = strftime(s, 1000, "%a", &tm);
+ test((size==3) && !cmp(s, "Thu"), "strftime test #2", s);
+
+ size = strftime(s, 1000, "%A", &tm);
+ test((size==8) && !cmp(s, "Thursday"), "strftime test #3", s);
+
+ size = strftime(s, 1000, "%b", &tm);
+ test((size==3) && !cmp(s, "Feb"), "strftime test #4", s);
+
+ size = strftime(s, 1000, "%B", &tm);
+ test((size==8) && !cmp(s, "February"),
+ "strftime test #5", s);
+
+ size = strftime(s, 1000, "%d", &tm);
+ test((size==2) && !cmp(s, "21"),
+ "strftime test #6", s);
+
+ size = strftime(s, 1000, "%H", &tm);
+ test((size==2) && !cmp(s, "20"),
+ "strftime test #7", s);
+
+ size = strftime(s, 1000, "%I", &tm);
+ test((size==2) && !cmp(s, "08"),
+ "strftime test #8", s);
+
+ size = strftime(s, 1000, "%j", &tm);
+ test((size==3) && !cmp(s, "052"),
+ "strftime test #9", s);
+
+ size = strftime(s, 1000, "%m", &tm);
+ test((size==2) && !cmp(s, "02"),
+ "strftime test #10", s);
+
+ size = strftime(s, 1000, "%M", &tm);
+ test((size==2) && !cmp(s, "23"),
+ "strftime test #11", s);
+
+ size = strftime(s, 1000, "%p", &tm);
+ test((size==2) && !cmp(s, "PM"),
+ "strftime test #12", s);
+
+ size = strftime(s, 1000, "%S", &tm);
+ test((size==2) && !cmp(s, "04"),
+ "strftime test #13", s);
+
+ size = strftime(s, 1000, "%U", &tm);
+ test((size==2) && !cmp(s, "07"),
+ "strftime test #14", s);
+
+ size = strftime(s, 1000, "%w", &tm);
+ test((size==1) && !cmp(s, "4"),
+ "strftime test #15", s);
+
+ size = strftime(s, 1000, "%W", &tm);
+ test((size==2) && !cmp(s, "07"),
+ "strftime test #16", s);
+
+ size = strftime(s, 1000, "%y", &tm);
+ test((size==2) && !cmp(s, "74"),
+ "strftime test #17", s);
+
+ size = strftime(s, 1000, "%Y", &tm);
+ test((size==4) && !cmp(s, "1974"),
+ "strftime test #18", s);
+
+ size = strftime(s, 1000, "%%", &tm);
+ test((size==1) && !cmp(s, "%"),
+ "strftime test #19", s);
+
+ size = strftime(s, 5, "%Y", &tm);
+ test((size==4) && !cmp(s, "1974"),
+ "strftime test #20", s);
+
+ size = strftime(s, 4, "%Y", &tm);
+ test((size==0), "strftime test #21", s);
+
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ size = strftime(s, 10, "%U", &tm);
+ test((size==2) && !cmp(s, "00"), "strftime test #22", s);
+
+ size = strftime(s, 10, "%W", &tm);
+ test((size==2) && !cmp(s, "00"), "strftime test #23", s);
+
+ // 1/1/1973 was a Sunday and is in CW 1
+ tm.tm_year = 73;
+ size = strftime(s, 10, "%W", &tm);
+ test((size==2) && !cmp(s, "01"), "strftime test #24", s);
+
+ // 1/1/1978 was a Monday and is in CW 1
+ tm.tm_year = 78;
+ size = strftime(s, 10, "%U", &tm);
+ test((size==2) && !cmp(s, "01"), "strftime test #25", s);
+
+ // 2/1/1999
+ tm.tm_year = 99;
+ tm.tm_yday = 1;
+ size = strftime(s, 10, "%G (%V)", &tm);
+ test((size==9) && !cmp(s, "1998 (53)"), "strftime test #26", s);
+
+ size = strftime(s, 10, "%g", &tm);
+ test((size==2) && !cmp(s, "98"), "strftime test #27", s);
+
+ // 30/12/1997
+ tm.tm_year = 97;
+ tm.tm_yday = 363;
+ size = strftime(s, 10, "%G (%V)", &tm);
+ test((size==9) && !cmp(s, "1998 (01)"), "strftime test #28", s);
+
+ size = strftime(s, 10, "%g", &tm);
+ test((size==2) && !cmp(s, "98"), "strftime test #29", s);
+ }
+ '''
+ self.do_run(src, '11111111111111111111111111111')
+
def test_intentional_fault(self):
# Some programs intentionally segfault themselves, we should compile that into a throw
src = r'''
@@ -5885,6 +6227,50 @@ def process(filename):
self.do_run(src, '100\n200\n13\n42\n',
post_build=add_pre_run_and_checks)
+ def test_dlfcn_self(self):
+ if Settings.USE_TYPED_ARRAYS == 1: return self.skip('Does not work with USE_TYPED_ARRAYS=1')
+ Settings.DLOPEN_SUPPORT = 1
+
+ src = r'''
+#include <stdio.h>
+#include <dlfcn.h>
+
+int global = 123;
+
+extern "C" __attribute__((noinline)) void foo(int x) {
+ printf("%d\n", x);
+}
+
+extern "C" __attribute__((noinline)) void repeatable() {
+ void* self = dlopen(NULL, RTLD_LAZY);
+ int* global_ptr = (int*)dlsym(self, "global");
+ void (*foo_ptr)(int) = (void (*)(int))dlsym(self, "foo");
+ foo_ptr(*global_ptr);
+ dlclose(self);
+}
+
+int main() {
+ repeatable();
+ repeatable();
+ return 0;
+}'''
+ def post(filename):
+ with open(filename) as f:
+ for line in f:
+ if 'var SYMBOL_TABLE' in line:
+ table = line
+ break
+ else:
+ raise Exception('Could not find symbol table!')
+ import json
+ table = json.loads(table[table.find('{'):table.rfind('}')+1])
+ actual = list(sorted(table.keys()))
+ # ensure there aren't too many globals; we don't want unnamed_addr
+ assert actual == ['_foo', '_global', '_main', '_repeatable'], \
+ "Symbol table does not match: %s" % actual
+
+ self.do_run(src, '123\n123', post_build=(None, post))
+
def test_rand(self):
return self.skip('rand() is now random') # FIXME
@@ -6605,6 +6991,26 @@ Pass: 0.000012 0.000012''')
'''
self.do_run(src, '2, , black\n2, ., #001100\n2, X, #111100');
+ def test_sscanf_6(self):
+ src = r'''
+ #include <stdio.h>
+ #include <string.h>
+ int main()
+ {
+ char *date = "18.07.2013w";
+ char c[10];
+ memset(c, 0, 10);
+ int y, m, d, i;
+ i = sscanf(date, "%d.%d.%4d%c", &d, &m, &y, c);
+ printf("date: %s; day %2d, month %2d, year %4d, extra: %c, %d\n", date, d, m, y, c[0], i);
+ i = sscanf(date, "%d.%d.%3c", &d, &m, c);
+ printf("date: %s; day %2d, month %2d, year %4d, extra: %s, %d\n", date, d, m, y, c, i);
+ }
+ '''
+ self.do_run(src, '''date: 18.07.2013w; day 18, month 7, year 2013, extra: w, 4
+date: 18.07.2013w; day 18, month 7, year 2013, extra: 201, 3
+''');
+
def test_sscanf_skip(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64")
@@ -6673,7 +7079,7 @@ def process(filename):
other.close()
src = open(path_from_root('tests', 'files.cpp'), 'r').read()
- self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n',
+ self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n',
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_files_m(self):
@@ -6705,7 +7111,7 @@ def process(filename):
return 0;
}
'''
- self.do_run(src, 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n', post_build=post)
+ self.do_run(src, 'got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', post_build=post)
def test_fwrite_0(self):
src = r'''
@@ -6799,23 +7205,19 @@ def process(filename):
self.do_run(src, 'success', force_c=True)
def test_stat(self):
- add_pre_run = '''
-def process(filename):
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- \'\'\'
- var f1 = FS.createFolder('/', 'test', true, true);
- var f2 = FS.createDataFile(f1, 'file', 'abcdef', true, true);
- var f3 = FS.createLink(f1, 'link', 'file', true, true);
- var f4 = FS.createDevice(f1, 'device', function(){}, function(){});
- f1.timestamp = f2.timestamp = f3.timestamp = f4.timestamp = new Date(1200000000000);
- \'\'\'
- )
- open(filename, 'w').write(src)
-'''
- src = open(path_from_root('tests', 'stat', 'src.c'), 'r').read()
- expected = open(path_from_root('tests', 'stat', 'output.txt'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run, extra_emscripten_args=['-H', 'libc/fcntl.h'])
+ Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
+ src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
+
+ def test_stat_chmod(self):
+ Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
+ src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
+
+ def test_stat_mknod(self):
+ Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
+ src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
def test_fcntl(self):
add_pre_run = '''
@@ -7197,18 +7599,8 @@ def process(filename):
self.do_run(src, expected)
def test_unistd_unlink(self):
- add_pre_run = '''
-def process(filename):
- import tools.shared as shared
- src = open(filename, 'r').read().replace(
- '// {{PRE_RUN_ADDITIONS}}',
- open(shared.path_from_root('tests', 'unistd', 'unlink.js'), 'r').read()
- )
- open(filename, 'w').write(src)
-'''
src = open(path_from_root('tests', 'unistd', 'unlink.c'), 'r').read()
- expected = open(path_from_root('tests', 'unistd', 'unlink.out'), 'r').read()
- self.do_run(src, expected, post_build=add_pre_run)
+ self.do_run(src, 'success', force_c=True)
def test_unistd_links(self):
add_pre_run = '''
@@ -9664,7 +10056,7 @@ def process(filename):
Settings.CORRECT_ROUNDINGS = 0
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here!
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick
- self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-6**5*') # We fail, since no fast shortcut for 32-bit unsigneds
+ self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-6**5*')
Settings.CORRECT_ROUNDINGS = 1
Settings.CORRECT_SIGNS = 1 # To be correct here, we need sign corrections as well
@@ -9678,7 +10070,7 @@ def process(filename):
Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake
self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*')
self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # Here we are lucky and also get the first one right
- self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here
+ self.do_run(src.replace('TYPE', 'unsigned int'), '*-2**2**-5**5*')
# And reverse the check with = 2
if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1
@@ -9854,6 +10246,11 @@ finalizing 3 (global == 0)
for k, v in self.env.iteritems():
del os.environ[k]
+ # clear global changes to Building
+ Building.COMPILER_TEST_OPTS = []
+ Building.COMPILER = CLANG
+ Building.LLVM_OPTS = 0
+
TT.tearDown = tearDown
def setUp(self):
@@ -10451,7 +10848,7 @@ f.close()
Popen([PYTHON, EMLINK, 'main.js', 'side.js', 'together.js'], stdout=PIPE).communicate()
assert os.path.exists('together.js')
for engine in JS_ENGINES:
- out = run_js('together.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True)
+ out = run_js('together.js', engine=engine, stderr=PIPE, full_output=True)
self.assertContained(expected, out)
if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out)
if first:
@@ -10642,6 +11039,51 @@ f.close()
args=['-I' + path_from_root('tests', 'bullet', 'src')])
+ def test_outline(self):
+ def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'):
+ print name
+
+ def measure_funcs(filename):
+ i = 0
+ start = -1
+ curr = '?'
+ ret = {}
+ for line in open(filename):
+ i += 1
+ if line.startswith('function '):
+ start = i
+ curr = line
+ elif line.startswith('}'):
+ size = i - start
+ if size > 100: ret[curr] = size
+ return ret
+
+ for outlining_limit in [500, 1000, 2000, 5000, 0]:
+ Popen([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2', '-g3', '-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args).communicate()
+ assert os.path.exists('test.js')
+ shutil.copyfile('test.js', '%d_test.js' % outlining_limit)
+ for engine in JS_ENGINES:
+ out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True)
+ self.assertContained(expected, out)
+ if engine == SPIDERMONKEY_ENGINE: self.validate_asmjs(out)
+ low = expected_ranges[outlining_limit][0]
+ seen = max(measure_funcs('test.js').values())
+ high = expected_ranges[outlining_limit][1]
+ print outlining_limit, ' ', low, '<=', seen, '<=', high
+ assert low <= seen <= high
+
+ test('zlib', path_from_root('tests', 'zlib', 'example.c'),
+ self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
+ open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
+ {
+ 500: (340, 345), # too big, need if-else chain flattening
+ 1000: (380, 390),
+ 2000: (395, 410),
+ 5000: (800, 1100),
+ 0: (1500, 1800)
+ },
+ args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
+
def test_symlink(self):
if os.name == 'nt':
return self.skip('Windows FS does not need to be tested for symlinks support, since it does not have them.')
@@ -11366,11 +11808,11 @@ f.close()
def test_js_optimizer(self):
for input, expected, passes in [
(path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
- ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost']),
+ ['hoistMultiples', 'loopOptimizer', 'removeAssignsToUndefined', 'simplifyExpressions']),
(path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
- ['simplifyExpressionsPre', 'optimizeShiftsConservative']),
+ ['simplifyExpressions', 'optimizeShiftsConservative']),
(path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
- ['simplifyExpressionsPre', 'optimizeShiftsAggressive']),
+ ['simplifyExpressions', '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-output.js')).read(),
['optimizeShiftsAggressive']),
@@ -11387,13 +11829,15 @@ f.close()
(path_from_root('tools', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tools', 'test-js-optimizer-asm-regs-min-output.js')).read(),
['asm', 'registerize']),
(path_from_root('tools', 'test-js-optimizer-asm-pre.js'), open(path_from_root('tools', 'test-js-optimizer-asm-pre-output.js')).read(),
- ['asm', 'simplifyExpressionsPre']),
+ ['asm', 'simplifyExpressions']),
(path_from_root('tools', 'test-js-optimizer-asm-last.js'), open(path_from_root('tools', 'test-js-optimizer-asm-last-output.js')).read(),
['asm', 'last']),
(path_from_root('tools', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tools', 'test-js-optimizer-asm-relocate-output.js')).read(),
['asm', 'relocate']),
- #(path_from_root('tools', 'test-js-optimizer-asm-outline.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline-output.js')).read(),
- # ['asm', 'outline']),
+ (path_from_root('tools', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline1-output.js')).read(),
+ ['asm', 'outline']),
+ (path_from_root('tools', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tools', 'test-js-optimizer-asm-outline2-output.js')).read(),
+ ['asm', 'outline']),
]:
print input
output = Popen(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
@@ -11630,6 +12074,24 @@ elif 'browser' in str(sys.argv):
print 'Running the browser tests. Make sure the browser allows popups from localhost.'
print
+ if 'audio' in sys.argv:
+ print
+ print 'Running the browser audio tests. Make sure to listen to hear the correct results!'
+ print
+
+ i = sys.argv.index('audio')
+ sys.argv = sys.argv[:i] + sys.argv[i+1:]
+ i = sys.argv.index('browser')
+ sys.argv = sys.argv[:i] + sys.argv[i+1:]
+ sys.argv += [
+ 'browser.test_sdl_audio',
+ 'browser.test_sdl_audio_mix_channels',
+ 'browser.test_sdl_audio_mix',
+ 'browser.test_sdl_audio_quickload',
+ 'browser.test_openal_playback',
+ 'browser.test_freealut'
+ ]
+
# Run a server and a web page. When a test runs, we tell the server about it,
# which tells the web page, which then opens a window with the test. Doing
# it this way then allows the page to close() itself when done.
@@ -12642,8 +13104,9 @@ Press any key to continue.'''
self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1')
def test_freealut(self):
- programs = self.get_library('freealut', os.path.join('examples', 'hello_world.bc'), make_args=['EXEEXT=.bc'])
+ programs = self.get_library('freealut', os.path.join('examples', '.libs', 'hello_world.bc'), make_args=['EXEEXT=.bc'])
for program in programs:
+ assert os.path.exists(program)
Popen([PYTHON, EMCC, '-O2', program, '-o', 'page.html']).communicate()
self.run_browser('page.html', 'You should hear "Hello World!"')
@@ -12907,6 +13370,9 @@ Press any key to continue.'''
def test_glshaderinfo(self):
self.btest('glshaderinfo.cpp', '1')
+ def test_glgetattachedshaders(self):
+ self.btest('glgetattachedshaders.c', '1')
+
def test_sdlglshader(self):
self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1'])
diff --git a/tests/sdl_rotozoom.c b/tests/sdl_rotozoom.c
index e81258d9..cdbdcc6f 100644
--- a/tests/sdl_rotozoom.c
+++ b/tests/sdl_rotozoom.c
@@ -6,7 +6,7 @@
#include "emscripten.h"
#endif
-const int numSprites = 8;
+const int numSprites = 9;
SDL_Surface *screen;
SDL_Surface *sprite[numSprites];
@@ -41,6 +41,7 @@ int main(int argc, char **argv) {
sprite[5] = rotozoomSurface(sprite[1], 45, 0.5, SMOOTHING_ON);
sprite[6] = zoomSurface(sprite[0], -0.5, 0.5, SMOOTHING_ON);
sprite[7] = zoomSurface(sprite[0], -0.5, -0.5, SMOOTHING_ON);
+ sprite[8] = rotozoomSurface(sprite[1], 0, 0.5, SMOOTHING_ON);
mainloop();
diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png
index 5c5dec2f..288dd303 100644
--- a/tests/sdl_rotozoom.png
+++ b/tests/sdl_rotozoom.png
Binary files differ
diff --git a/tests/stat/output.txt b/tests/stat/output.txt
deleted file mode 100644
index 1e6ae74e..00000000
--- a/tests/stat/output.txt
+++ /dev/null
@@ -1,202 +0,0 @@
---stat FOLDER--
-ret: 0
-errno: 0
-st_dev: 1
-st_ino: 2
-st_mode: 040777
-st_nlink: 1
-st_rdev: 0
-st_size: 4096
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 1
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 1
-S_ISFIFO: 0
-S_ISREG: 0
-S_ISLNK: 0
-S_ISSOCK: 0
-
---stat FILE--
-ret: 0
-errno: 0
-st_dev: 1
-st_ino: 3
-st_mode: 0100777
-st_nlink: 1
-st_rdev: 0
-st_size: 6
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 1
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 1
-S_ISLNK: 0
-S_ISSOCK: 0
-
---stat DEVICE--
-ret: 0
-errno: 0
-st_dev: 5
-st_ino: 5
-st_mode: 020777
-st_nlink: 1
-st_rdev: 5
-st_size: 0
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 0
-S_ISBLK: 0
-S_ISCHR: 1
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 0
-S_ISLNK: 0
-S_ISSOCK: 0
-
---stat LINK--
-ret: 0
-errno: 0
-st_dev: 1
-st_ino: 3
-st_mode: 0100777
-st_nlink: 1
-st_rdev: 0
-st_size: 6
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 1
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 1
-S_ISLNK: 0
-S_ISSOCK: 0
-
---lstat LINK--
-ret: 0
-errno: 0
-st_dev: 1
-st_ino: 4
-st_mode: 0120777
-st_nlink: 1
-st_rdev: 0
-st_size: 4
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 1
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 0
-S_ISLNK: 1
-S_ISSOCK: 0
-
---fstat FILE--
-ret: 0
-errno: 0
-st_dev: 1
-st_ino: 3
-st_mode: 0100777
-st_nlink: 1
-st_rdev: 0
-st_size: 6
-st_atime: 1200000000
-st_mtime: 1200000000
-st_ctime: 1200000000
-st_blksize: 4096
-st_blocks: 1
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 1
-S_ISLNK: 0
-S_ISSOCK: 0
-
---chmod FILE--
-ret: 0
-errno: 0
-st_mode: 0100222
-st_mtime changed: 1
-
---fchmod FILE--
-ret: 0
-errno: 0
-st_mode: 0100777
-st_mtime changed: 1
-
---chmod FOLDER--
-ret: 0
-errno: 0
-st_mode: 040555
-st_mtime changed: 1
-
---chmod LINK--
-ret: 0
-errno: 0
-st_mode: 0100000
-
---mkdir--
-ret: 0
-errno: 0
-st_mode: 040777
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 1
-S_ISFIFO: 0
-S_ISREG: 0
-S_ISLNK: 0
-S_ISSOCK: 0
-
---mknod FILE--
-ret: 0
-errno: 0
-st_mode: 0100777
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 0
-S_ISFIFO: 0
-S_ISREG: 1
-S_ISLNK: 0
-S_ISSOCK: 0
-
---mknod FOLDER--
-ret: 0
-errno: 0
-st_mode: 040777
-S_ISBLK: 0
-S_ISCHR: 0
-S_ISDIR: 1
-S_ISFIFO: 0
-S_ISREG: 0
-S_ISLNK: 0
-S_ISSOCK: 0
-
---mknod FIFO--
-ret: -1
-errno: 22
-
---mknod DEVICE--
-ret: -1
-errno: 22
-
---mkfifo--
-ret: -1
-errno: 30
diff --git a/tests/stat/src.c b/tests/stat/src.c
deleted file mode 100644
index dc5a0198..00000000
--- a/tests/stat/src.c
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
-Note: Hardcoded st_ino values etc. may change with minor changes to the library impl.
- In such an event, we will need to update output.txt here.
-*/
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-int main() {
- struct stat s;
-
- printf("--stat FOLDER--\n");
- printf("ret: %d\n", stat("/test", &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--stat FILE--\n");
- printf("ret: %d\n", stat("/test/file", &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--stat DEVICE--\n");
- printf("ret: %d\n", stat("/test/device", &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--stat LINK--\n");
- printf("ret: %d\n", stat("/test/link", &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--lstat LINK--\n");
- printf("ret: %d\n", lstat("/test/link", &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--fstat FILE--\n");
- printf("ret: %d\n", fstat(open("/test/file", O_RDONLY, 0777), &s));
- printf("errno: %d\n", errno);
- printf("st_dev: %lu\n", s.st_dev);
- printf("st_ino: %lu\n", s.st_ino);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_nlink: %d\n", s.st_nlink);
- printf("st_rdev: %lu\n", s.st_rdev);
- printf("st_size: %ld\n", s.st_size);
- printf("st_atime: %ld\n", s.st_atime);
- printf("st_mtime: %ld\n", s.st_mtime);
- printf("st_ctime: %ld\n", s.st_ctime);
- printf("st_blksize: %ld\n", s.st_blksize);
- printf("st_blocks: %ld\n", s.st_blocks);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--chmod FILE--\n");
- printf("ret: %d\n", chmod("/test/file", 0200));
- printf("errno: %d\n", errno);
- stat("/test/file", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l);
- memset(&s, 0, sizeof s);
-
- printf("\n--fchmod FILE--\n");
- printf("ret: %d\n", fchmod(open("/test/file", O_WRONLY, 0777), 0777));
- printf("errno: %d\n", errno);
- stat("/test/file", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l);
- memset(&s, 0, sizeof s);
-
- printf("\n--chmod FOLDER--\n");
- printf("ret: %d\n", chmod("/test", 0400));
- printf("errno: %d\n", errno);
- stat("/test", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("st_mtime changed: %d\n", s.st_mtime != 1200000000l);
- memset(&s, 0, sizeof s);
-
- printf("\n--chmod LINK--\n");
- printf("ret: %d\n", chmod("/test/link", 0000));
- printf("errno: %d\n", errno);
- stat("/test/file", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- memset(&s, 0, sizeof s);
-
- // Make sure we can create stuff in the root.
- chmod("/", 0777);
-
- printf("\n--mkdir--\n");
- printf("ret: %d\n", mkdir("/test-mkdir", 0777));
- printf("errno: %d\n", errno);
- stat("/test-mkdir", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--mknod FILE--\n");
- printf("ret: %d\n", mknod("/test-mknod-file", S_IFREG | 0777, 0));
- printf("errno: %d\n", errno);
- stat("/test-mknod-file", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--mknod FOLDER--\n");
- printf("ret: %d\n", mknod("/test-mknod-dir", S_IFDIR | 0777, 0));
- printf("errno: %d\n", errno);
- stat("/test-mknod-dir", &s);
- printf("st_mode: 0%o\n", s.st_mode);
- printf("S_ISBLK: %d\n", S_ISBLK(s.st_mode));
- printf("S_ISCHR: %d\n", S_ISCHR(s.st_mode));
- printf("S_ISDIR: %d\n", S_ISDIR(s.st_mode));
- printf("S_ISFIFO: %d\n", S_ISFIFO(s.st_mode));
- printf("S_ISREG: %d\n", S_ISREG(s.st_mode));
- printf("S_ISLNK: %d\n", S_ISLNK(s.st_mode));
- printf("S_ISSOCK: %d\n", S_ISSOCK(s.st_mode));
- memset(&s, 0, sizeof s);
-
- printf("\n--mknod FIFO--\n");
- printf("ret: %d\n", mknod("/test-mknod-fifo", S_IFIFO | 0777, 0));
- printf("errno: %d\n", errno);
-
- printf("\n--mknod DEVICE--\n");
- printf("ret: %d\n", mknod("/test-mknod-device", S_IFCHR | 0777, 123));
- printf("errno: %d\n", errno);
-
- printf("\n--mkfifo--\n");
- printf("ret: %d\n", mkfifo("/test-mkfifo", 0777));
- printf("errno: %d\n", errno);
-
- return 0;
-}
diff --git a/tests/stat/test_chmod.c b/tests/stat/test_chmod.c
new file mode 100644
index 00000000..94e6c12b
--- /dev/null
+++ b/tests/stat/test_chmod.c
@@ -0,0 +1,153 @@
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ create_file("file", "abcdef", 0777);
+ symlink("file", "file-link");
+ // some platforms use 777, some use 755 by default for symlinks
+ // make sure we're using 777 for the test
+ lchmod("file-link", 0777);
+ mkdir("folder", 0777);
+}
+
+void cleanup() {
+ unlink("file-link");
+ unlink("file");
+ rmdir("folder");
+}
+
+void test() {
+ int err;
+ int lastctime;
+ struct stat s;
+
+ //
+ // chmod a file
+ //
+ // get the current ctime for the file
+ memset(&s, 0, sizeof s);
+ stat("file", &s);
+ lastctime = s.st_ctime;
+ sleep(1);
+
+ // do the actual chmod
+ err = chmod("file", 0200);
+ assert(!err);
+
+ memset(&s, 0, sizeof s);
+ stat("file", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0222 | S_IFREG));
+#else
+ assert(s.st_mode == (0200 | S_IFREG));
+#endif
+ assert(s.st_ctime != lastctime);
+
+ //
+ // fchmod a file
+ //
+ lastctime = s.st_ctime;
+ sleep(1);
+
+ err = fchmod(open("file", O_WRONLY), 0100);
+ assert(!err);
+
+ memset(&s, 0, sizeof s);
+ stat("file", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0000 | S_IFREG));
+#else
+ assert(s.st_mode == (0100 | S_IFREG));
+#endif
+ assert(s.st_ctime != lastctime);
+
+ //
+ // chmod a folder
+ //
+ // get the current ctime for the folder
+ memset(&s, 0, sizeof s);
+ stat("folder", &s);
+ lastctime = s.st_ctime;
+ sleep(1);
+
+ // do the actual chmod
+ err = chmod("folder", 0300);
+ assert(!err);
+ memset(&s, 0, sizeof s);
+ stat("folder", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0222 | S_IFDIR));
+#else
+ assert(s.st_mode == (0300 | S_IFDIR));
+#endif
+ assert(s.st_ctime != lastctime);
+
+ //
+ // chmod a symlink's target
+ //
+ err = chmod("file-link", 0400);
+ assert(!err);
+
+ // make sure the file it references changed
+ stat("file-link", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0555 | S_IFREG));
+#else
+ assert(s.st_mode == (0400 | S_IFREG));
+#endif
+
+ // but the link didn't
+ lstat("file-link", &s);
+ assert(s.st_mode == (0777 | S_IFLNK));
+
+ //
+ // chmod the actual symlink
+ //
+ err = lchmod("file-link", 0500);
+ assert(!err);
+
+ // make sure the file it references didn't change
+ stat("file-link", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0555 | S_IFREG));
+#else
+ assert(s.st_mode == (0400 | S_IFREG));
+#endif
+
+ // but the link did
+ lstat("file-link", &s);
+#if USE_OLD_FS
+ assert(s.st_mode == (0555 | S_IFLNK));
+#else
+ assert(s.st_mode == (0500 | S_IFLNK));
+#endif
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/stat/test_mknod.c b/tests/stat/test_mknod.c
new file mode 100644
index 00000000..4cff57d9
--- /dev/null
+++ b/tests/stat/test_mknod.c
@@ -0,0 +1,96 @@
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void setup() {
+ mkdir("folder-readonly", 0555);
+}
+
+void cleanup() {
+ unlink("mknod-file");
+ unlink("mknod-device");
+ rmdir("folder");
+ rmdir("folder-readonly");
+}
+
+void test() {
+ int err;
+ struct stat s;
+
+ //
+ // mknod
+ // mknod is _extremely_ unportable for anything other
+ // than a FIFO. so, the tests are disabled when running
+ // natively as they'd be utterly inconsistent.
+ //
+#if EMSCRIPTEN
+
+ // mknod a folder
+ err = mknod("mknod-folder", S_IFDIR | 0777, 0);
+ assert(err);
+ assert(errno == EINVAL);
+
+ // mknod fifo
+ err = mknod("mknod-fifo", S_IFIFO | 0777, 0);
+ assert(err);
+ assert(errno == EPERM);
+
+ // mknod a file
+ err = mknod("mknod-file", S_IFREG | 0777, 0);
+ assert(!err);
+ memset(&s, 0, sizeof s);
+ stat("mknod-file", &s);
+ assert(S_ISREG(s.st_mode));
+
+ // mknod a character device
+ err = mknod("mknod-device", S_IFCHR | 0777, 123);
+#if USE_OLD_FS
+ assert(err);
+ assert(errno == EPERM);
+#else
+ assert(!err);
+ memset(&s, 0, sizeof s);
+ stat("mknod-device", &s);
+ assert(S_ISCHR(s.st_mode));
+#endif
+
+#endif
+
+ //
+ // mkdir
+ //
+ // can't mkdir in a readonly dir
+ err = mkdir("folder-readonly/subfolder", 0777);
+ assert(err);
+ assert(errno == EACCES);
+
+ // regular creation
+ err = mkdir("folder", 0777);
+ assert(!err);
+ memset(&s, 0, sizeof s);
+ stat("folder", &s);
+ assert(S_ISDIR(s.st_mode));
+
+ // try to re-create the same folder
+ err = mkdir("folder", 0777);
+ assert(err);
+ assert(errno == EEXIST);
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/stat/test_stat.c b/tests/stat/test_stat.c
new file mode 100644
index 00000000..14e88370
--- /dev/null
+++ b/tests/stat/test_stat.c
@@ -0,0 +1,167 @@
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ struct utimbuf t = {1200000000, 1200000000};
+
+ mkdir("folder", 0777);
+ create_file("folder/file", "abcdef", 0777);
+ symlink("file", "folder/file-link");
+
+ utime("folder/file", &t);
+ utime("folder", &t);
+}
+
+void cleanup() {
+ unlink("folder/file");
+ unlink("folder/file-link");
+ rmdir("folder");
+}
+
+void test() {
+ int err;
+ struct stat s;
+
+ // stat a folder
+ memset(&s, 0, sizeof(s));
+ err = stat("folder", &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISDIR(s.st_mode));
+ assert(s.st_nlink);
+ assert(s.st_rdev == 0);
+ assert(s.st_size);
+ assert(s.st_atime == 1200000000);
+ assert(s.st_mtime == 1200000000);
+ assert(s.st_ctime);
+#ifdef EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 1);
+#endif
+
+ // stat a file
+ memset(&s, 0, sizeof(s));
+ err = stat("folder/file", &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISREG(s.st_mode));
+ assert(s.st_nlink);
+ assert(s.st_rdev == 0);
+ assert(s.st_size == 6);
+ assert(s.st_atime == 1200000000);
+ assert(s.st_mtime == 1200000000);
+ assert(s.st_ctime);
+#if EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 1);
+#endif
+
+ // fstat a file (should match file stat from above)
+ memset(&s, 0, sizeof(s));
+ err = fstat(open("folder/file", O_RDONLY), &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISREG(s.st_mode));
+ assert(s.st_nlink);
+ assert(s.st_rdev == 0);
+ assert(s.st_size == 6);
+ assert(s.st_atime == 1200000000);
+ assert(s.st_mtime == 1200000000);
+ assert(s.st_ctime);
+#if EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 1);
+#endif
+
+ // stat a device
+ memset(&s, 0, sizeof(s));
+ err = stat("/dev/null", &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISCHR(s.st_mode));
+ assert(s.st_nlink);
+#if !USE_OLD_FS
+ // old FS doesn't store proper device ids
+#ifndef __APPLE__
+ // mac uses makedev(3, 2) for /dev/null
+ assert(s.st_rdev == makedev(1, 3));
+#endif
+#endif
+ assert(!s.st_size);
+ assert(s.st_atime);
+ assert(s.st_mtime);
+ assert(s.st_ctime);
+#if EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 0);
+#endif
+
+ // stat a link (should match the file stat from above)
+ memset(&s, 0, sizeof(s));
+ err = stat("folder/file-link", &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISREG(s.st_mode));
+ assert(s.st_nlink);
+ assert(s.st_rdev == 0);
+ assert(s.st_size == 6);
+ assert(s.st_atime == 1200000000);
+ assert(s.st_mtime == 1200000000);
+ assert(s.st_ctime);
+#if EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 1);
+#endif
+
+ // lstat a link (should NOT match the file stat from above)
+ memset(&s, 0, sizeof(s));
+ err = lstat("folder/file-link", &s);
+ assert(!err);
+ assert(s.st_dev);
+ assert(s.st_ino);
+ assert(S_ISLNK(s.st_mode));
+ assert(s.st_nlink);
+ assert(s.st_rdev == 0);
+ assert(s.st_size == 4); // strlen("file")
+ assert(s.st_atime != 1200000000); // should NOT match the utime call we did for dir/file
+ assert(s.st_mtime != 1200000000);
+ assert(s.st_ctime);
+#if EMSCRIPTEN
+ assert(s.st_blksize == 4096);
+ assert(s.st_blocks == 1);
+#endif
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/unistd/unlink.c b/tests/unistd/unlink.c
index 3f7d84b6..87252da2 100644
--- a/tests/unistd/unlink.c
+++ b/tests/unistd/unlink.c
@@ -1,35 +1,138 @@
-#include <stdio.h>
+#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include <sys/stat.h>
-int main() {
- char* files[] = {"/device", "/file", "/file-forbidden", "/noexist"};
- char* folders[] = {"/empty", "/empty-forbidden", "/full"};
- int i;
-
- for (i = 0; i < sizeof files / sizeof files[0]; i++) {
- printf("access(%s) before: %d\n", files[i], access(files[i], F_OK));
- rmdir(files[i]);
- printf("errno: %d\n", errno);
- errno = 0;
- printf("access(%s) after rmdir: %d\n", files[i], access(files[i], F_OK));
- unlink(files[i]);
- printf("errno: %d\n", errno);
- errno = 0;
- printf("access(%s) after unlink: %d\n\n", files[i], access(files[i], F_OK));
- }
-
- for (i = 0; i < sizeof folders / sizeof folders[0]; i++) {
- printf("access(%s) before: %d\n", folders[i], access(folders[i], F_OK));
- unlink(folders[i]);
- printf("errno: %d\n", errno);
- errno = 0;
- printf("access(%s) after unlink: %d\n", folders[i], access(folders[i], F_OK));
- rmdir(folders[i]);
- printf("errno: %d\n", errno);
- errno = 0;
- printf("access(%s) after rmdir: %d\n\n", folders[i], access(folders[i], F_OK));
- }
-
- return 0;
+static void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ create_file("file", "test", 0777);
+ create_file("file1", "test", 0777);
+ symlink("file1", "file1-link");
+ mkdir("dir-empty", 0777);
+ symlink("dir-empty", "dir-empty-link");
+ mkdir("dir-readonly", 0777);
+ create_file("dir-readonly/anotherfile", "test", 0777);
+ mkdir("dir-readonly/anotherdir", 0777);
+ chmod("dir-readonly", 0555);
+ mkdir("dir-full", 0777);
+ create_file("dir-full/anotherfile", "test", 0777);
+}
+
+void cleanup() {
+ unlink("file");
+ unlink("file1");
+ unlink("file1-link");
+ rmdir("dir-empty");
+ unlink("dir-empty-link");
+ chmod("dir-readonly", 0777);
+ unlink("dir-readonly/anotherfile");
+ rmdir("dir-readonly/anotherdir");
+ rmdir("dir-readonly");
+ unlink("dir-full/anotherfile");
+ rmdir("dir-full");
+}
+
+void test() {
+ int err;
+ char buffer[512];
+
+ //
+ // test unlink
+ //
+ err = unlink("noexist");
+ assert(err == -1);
+ assert(errno == ENOENT);
+
+ err = unlink("dir-readonly");
+ assert(err == -1);
+#ifdef __linux__
+ assert(errno == EISDIR);
+#else
+ assert(errno == EPERM);
+#endif
+
+ err = unlink("dir-readonly/anotherfile");
+ assert(err == -1);
+ assert(errno == EACCES);
+
+ // try unlinking the symlink first to make sure
+ // we don't follow the link
+ err = unlink("file1-link");
+ assert(!err);
+ err = access("file1", F_OK);
+ assert(!err);
+ err = access("file1-link", F_OK);
+ assert(err == -1);
+
+ err = unlink("file");
+ assert(!err);
+ err = access("file", F_OK);
+ assert(err == -1);
+
+ //
+ // test rmdir
+ //
+ err = rmdir("noexist");
+ assert(err == -1);
+ assert(errno == ENOENT);
+
+ err = rmdir("file1");
+ assert(err == -1);
+ assert(errno == ENOTDIR);
+
+ err = rmdir("dir-readonly/anotherdir");
+ assert(err == -1);
+ assert(errno == EACCES);
+
+ err = rmdir("dir-full");
+ assert(err == -1);
+ assert(errno == ENOTEMPTY);
+
+ // test removing the cwd / root. The result isn't specified by
+ // POSIX, but Linux seems to set EBUSY in both cases.
+#ifndef __APPLE__
+ getcwd(buffer, sizeof(buffer));
+ err = rmdir(buffer);
+ assert(err == -1);
+ assert(errno == EBUSY);
+#endif
+ err = rmdir("/");
+ assert(err == -1);
+#ifdef __APPLE__
+ assert(errno == EISDIR);
+#else
+ assert(errno == EBUSY);
+#endif
+
+ err = rmdir("dir-empty-link");
+ assert(err == -1);
+ assert(errno == ENOTDIR);
+
+ err = rmdir("dir-empty");
+ assert(!err);
+ err = access("dir-empty", F_OK);
+ assert(err == -1);
+
+ puts("success");
}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tests/unistd/unlink.js b/tests/unistd/unlink.js
deleted file mode 100644
index c2366080..00000000
--- a/tests/unistd/unlink.js
+++ /dev/null
@@ -1,7 +0,0 @@
-FS.createDevice('/', 'device', function() {}, function() {});
-FS.createDataFile('/', 'file', 'test', true, true);
-FS.createDataFile('/', 'file-forbidden', 'test', true, false);
-FS.createFolder('/', 'empty', true, true);
-FS.createFolder('/', 'empty-forbidden', true, false);
-FS.createFolder('/', 'full', true, true);
-FS.createFolder('/full', 'junk', true, true);
diff --git a/tests/unistd/unlink.out b/tests/unistd/unlink.out
deleted file mode 100644
index f7a894cb..00000000
--- a/tests/unistd/unlink.out
+++ /dev/null
@@ -1,42 +0,0 @@
-access(/device) before: 0
-errno: 20
-access(/device) after rmdir: 0
-errno: 0
-access(/device) after unlink: -1
-
-access(/file) before: 0
-errno: 20
-access(/file) after rmdir: 0
-errno: 0
-access(/file) after unlink: -1
-
-access(/file-forbidden) before: 0
-errno: 13
-access(/file-forbidden) after rmdir: 0
-errno: 13
-access(/file-forbidden) after unlink: 0
-
-access(/noexist) before: -1
-errno: 2
-access(/noexist) after rmdir: -1
-errno: 2
-access(/noexist) after unlink: -1
-
-access(/empty) before: 0
-errno: 21
-access(/empty) after unlink: 0
-errno: 0
-access(/empty) after rmdir: -1
-
-access(/empty-forbidden) before: 0
-errno: 21
-access(/empty-forbidden) after unlink: 0
-errno: 13
-access(/empty-forbidden) after rmdir: 0
-
-access(/full) before: 0
-errno: 21
-access(/full) after unlink: 0
-errno: 90
-access(/full) after rmdir: 0
-
diff --git a/tools/file_packager.py b/tools/file_packager.py
index cc030f59..575e5957 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -330,15 +330,18 @@ for file_ in data_files:
if file_['mode'] == 'embed':
# Embed
data = map(ord, open(file_['srcpath'], 'rb').read())
- str_data = ''
- chunk_size = 10240
- while len(data) > 0:
- chunk = data[:chunk_size]
- data = data[chunk_size:]
- if not str_data:
- str_data = str(chunk)
- else:
- str_data += '.concat(' + str(chunk) + ')'
+ if not data:
+ str_data = '[]'
+ else:
+ str_data = ''
+ chunk_size = 10240
+ while len(data) > 0:
+ chunk = data[:chunk_size]
+ data = data[chunk_size:]
+ if not str_data:
+ str_data = str(chunk)
+ else:
+ str_data += '.concat(' + str(chunk) + ')'
code += '''Module['FS_createDataFile']('/%s', '%s', %s, true, true);\n''' % (os.path.dirname(filename), os.path.basename(filename), str_data)
elif file_['mode'] == 'preload':
# Preload
diff --git a/tools/find_bigfuncs.py b/tools/find_bigfuncs.py
index ebff8b6e..31825544 100644
--- a/tools/find_bigfuncs.py
+++ b/tools/find_bigfuncs.py
@@ -1,15 +1,14 @@
'''
-Simple tool to find big functions in an .ll file. Anything over i64 is of interest.
+Simple tool to find big functions in an .ll file.
'''
import os, sys, re
filename = sys.argv[1]
i = 0
-maxx = -1
-maxxest = '?'
start = -1
curr = '?'
+data = []
for line in open(filename):
i += 1
if line.startswith('function '):
@@ -17,7 +16,7 @@ for line in open(filename):
curr = line
elif line.startswith('}'):
size = i - start
- if size > maxx:
- maxx = size
- maxxest = curr
-print maxx, 'lines in', maxxest
+ data.append([curr, size]);
+data.sort(lambda x, y: x[1] - y[1])
+print ''.join(['%6d : %s' % (x[1], x[0]) for x in data])
+
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 71a59921..a7f1e4e1 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -410,7 +410,7 @@ function removeUnneededLabelSettings(ast) {
var USEFUL_BINARY_OPS = set('<<', '>>', '|', '&', '^');
var COMPARE_OPS = set('<', '<=', '>', '>=', '==', '===', '!=', '!==');
-function simplifyExpressionsPre(ast) {
+function simplifyExpressions(ast) {
// Simplify common expressions used to perform integer conversion operations
// in cases where no conversion is needed.
function simplifyIntegerConversions(ast) {
@@ -793,6 +793,7 @@ function simplifyExpressionsPre(ast) {
joinAdditions(func);
// simplifyZeroComp(func); TODO: investigate performance
if (asm) asmOpts(func);
+ simplifyNotComps(func);
});
}
@@ -1158,10 +1159,6 @@ function simplifyNotComps(ast) {
simplifyNotCompsPass = false;
}
-function simplifyExpressionsPost(ast) {
- simplifyNotComps(ast);
-}
-
var NO_SIDE_EFFECTS = set('num', 'name');
function hasSideEffects(node) { // this is 99% incomplete!
@@ -1540,24 +1537,22 @@ function detectAsmCoercion(node, asmInfo) {
// for params, +x vs x|0, for vars, 0.0 vs 0
if (node[0] === 'num' && node[1].toString().indexOf('.') >= 0) return ASM_DOUBLE;
if (node[0] === 'unary-prefix') return ASM_DOUBLE;
- if (asmInfo && node[0] == 'name') {
- if (node[1] in asmInfo.vars) return asmInfo.vars[node[1]];
- if (node[1] in asmInfo.params) return asmInfo.params[node[1]];
- }
+ if (asmInfo && node[0] == 'name') return getAsmType(node[1], asmInfo);
return ASM_INT;
}
-function makeAsmParamCoercion(param, type) {
- return type === ASM_INT ? ['binary', '|', ['name', param], ['num', 0]] : ['unary-prefix', '+', ['name', param]];
+function makeAsmCoercion(node, type) {
+ return type === ASM_INT ? ['binary', '|', node, ['num', 0]] : ['unary-prefix', '+', node];
}
function makeAsmVarDef(v, type) {
return [v, type === ASM_INT ? ['num', 0] : ['unary-prefix', '+', ['num', 0]]];
}
-function getAsmType(asmInfo, name) {
+function getAsmType(name, asmInfo) {
if (name in asmInfo.vars) return asmInfo.vars[name];
- return asmInfo.params[name];
+ if (name in asmInfo.params) return asmInfo.params[name];
+ assert(false, 'unknown var ' + name);
}
function normalizeAsm(func) {
@@ -1658,7 +1653,7 @@ function denormalizeAsm(func, data) {
// add param coercions
var next = 0;
func[2].forEach(function(param) {
- stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmParamCoercion(param, data.params[param])]];
+ stats[next++] = ['stat', ['assign', true, ['name', param], makeAsmCoercion(['name', param], data.params[param])]];
});
// add variable definitions
var varDefs = [];
@@ -1673,6 +1668,37 @@ function denormalizeAsm(func, data) {
//printErr('denormalized \n\n' + astToSrc(func) + '\n\n');
}
+function getFirstIndexInNormalized(func, data) {
+ // In a normalized asm function, return the index of the first element that is not not defs or annotation
+ var stats = func[3];
+ var i = stats.length-1;
+ while (i >= 0) {
+ var stat = stats[i];
+ if (stat[0] == 'var') break;
+ i--;
+ }
+ return i+1;
+}
+
+function getStackBumpNode(ast) {
+ var found = null;
+ traverse(ast, function(node, type) {
+ if (type === 'assign' && node[2][0] === 'name' && node[2][1] === 'STACKTOP') {
+ var value = node[3];
+ if (value[0] === 'name') return true;
+ assert(value[0] == 'binary' && value[1] == '|' && value[2][0] == 'binary' && value[2][1] == '+' && value[2][2][0] == 'name' && value[2][2][1] == 'STACKTOP' && value[2][3][0] == 'num');
+ found = node;
+ return true;
+ }
+ });
+ return found;
+}
+
+function getStackBumpSize(ast) {
+ var node = getStackBumpNode(ast);
+ return node ? node[3][2][3][1] : 0;
+}
+
// Very simple 'registerization', coalescing of variables into a smaller number,
// as part of minification. Globals-level minification began in a previous pass,
// we receive extraInfo which tells us how to rename globals. (Only in asm.js.)
@@ -2981,32 +3007,37 @@ function outline(ast) {
stack.push(name);
}
asmData.stackPos = {};
+ var stackSize = getStackBumpSize(func);
+ if (stackSize % 8 === 0) stackSize += 8 - (stackSize % 8);
for (var i = 0; i < stack.length; i++) {
- asmData.stackPos[stack[i]] = i*8;
- }
- // Reserve an extra two spots: one for control flow var, the other for control flow data
- asmData.stackSize = (stack.length + 2)*8;
- asmData.controlStackPos = asmData.stackSize - 16;
- asmData.controlDataStackPos = asmData.stackSize - 8;
+ asmData.stackPos[stack[i]] = stackSize + i*8;
+ }
+ // Reserve an extra two spots per possible outlining: one for control flow var, the other for control flow data
+ // The control variables are zeroed out when calling an outlined function, and after using
+ // the value after they return.
+ asmData.maxOutlinings = Math.round(1.5*measureSize(func)/sizeToOutline);
+ asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8;
+ asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 };
+ asmData.controlDataStackPos = function(i) { return stackSize + (stack.length + i)*8 + 4 };
asmData.splitCounter = 0;
}
// Analyze uses - reads and writes - of variables in part of the AST of a function
function analyzeCode(func, asmData, ast) {
- var labels = {};
+ var labels = {}; // labels defined in this code
var labelCounter = 1; // 0 means no label
traverse(ast, function(node, type) {
- if ((type == 'label' || type in LOOP_FLOW) && node[1] && !(node[1] in labels)) {
+ if (type == 'label' && !(node[1] in labels)) {
labels[node[1]] = labelCounter++;
}
});
var writes = {};
- var appearances = {};
- var hasReturn = false, hasBreak = false, hasContinue = false;
+ var namings = {};
+ var hasReturn = false, hasReturnInt = false, hasReturnDouble = false, hasBreak = false, hasContinue = false;
var breaks = {}; // set of labels we break or continue
- var continues = {}; // to. '0' is an unlabeled one
+ var continues = {}; // to (name -> id, just like labels)
var breakCapturers = 0;
var continueCapturers = 0;
@@ -3014,27 +3045,32 @@ function outline(ast) {
if (type == 'assign' && node[2][0] == 'name') {
var name = node[2][1];
if (name in asmData.vars || name in asmData.params) {
- writes[name] = 0;
- appearances[name] = (appearances[name] || 0) - 1; // this appearance is a definition, offset the counting later
+ writes[name] = (writes[name] || 0) + 1;
}
} else if (type == 'name') {
var name = node[1];
if (name in asmData.vars || name in asmData.params) {
- appearances[name] = (appearances[name] || 0) + 1;
+ namings[name] = (namings[name] || 0) + 1;
}
} else if (type == 'return') {
- hasReturn = true;
+ if (!node[1]) {
+ hasReturn = true;
+ } else if (detectAsmCoercion(node[1]) == ASM_INT) {
+ hasReturnInt = true;
+ } else {
+ hasReturnDouble = true;
+ }
} else if (type == 'break') {
var label = node[1] || 0;
if (!label && breakCapturers > 0) return; // no label, and captured
if (label && (label in labels)) return; // label, and defined in this code, so captured
- breaks[label || 0] = 0;
+ if (label) breaks[label] = labelCounter++;
hasBreak = true;
} else if (type == 'continue') {
var label = node[1] || 0;
if (!label && continueCapturers > 0) return; // no label, and captured
if (label && (label in labels)) return; // label, and defined in this code, so captured
- continues[label || 0] = 0;
+ if (label) continues[label] = labelCounter++;
hasContinue = true;
} else {
if (type in BREAK_CAPTURERS) {
@@ -3052,14 +3088,15 @@ function outline(ast) {
continueCapturers--;
}
});
+ assert(hasReturn + hasReturnInt + hasReturnDouble <= 1);
var reads = {};
-
- for (var name in appearances) {
- if (appearances[name] > 0) reads[name] = 0;
+ for (var v in namings) {
+ var actualReads = namings[v] - (writes[v] || 0);
+ if (actualReads > 0) reads[v] = actualReads;
}
- return { writes: writes, reads: reads, hasReturn: hasReturn, breaks: breaks, continues: continues, labels: labels };
+ return { writes: writes, reads: reads, hasReturn: hasReturn, hasReturnInt: hasReturnInt, hasReturnDouble: hasReturnDouble, hasBreak: hasBreak, hasContinue: hasContinue, breaks: breaks, continues: continues, labels: labels };
}
function makeAssign(dst, src) {
@@ -3076,76 +3113,106 @@ function outline(ast) {
function makeComparison(left, comp, right) {
return ['binary', comp, left, right];
}
+ function makeSwitch(value, cases) {
+ return ['switch', value, cases];
+ }
var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
var sizeToOutline = extraInfo.sizeToOutline;
var level = 0;
+ var outliningParents = {}; // function name => parent it was outlined from
+
function doOutline(func, asmData, stats, start, end) {
- printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length]);
+ if (asmData.splitCounter === asmData.maxOutlinings) return [];
+ if (!extraInfo.allowCostlyOutlines) var originalStats = copy(stats);
var code = stats.slice(start, end+1);
- var newIdent = func[1] + '$' + (asmData.splitCounter++);
- // add spills and reads before and after the call to the outlined code, and in the outlined code itself
+ var funcSize = measureSize(func);
+ var outlineIndex = asmData.splitCounter++;
+ var newIdent = func[1] + '$' + outlineIndex;
+ // analyze variables, and find 'owned' variables - that only appear in the outlined code, and do not need any spill support
var codeInfo = analyzeCode(func, asmData, code);
+ var allCodeInfo = analyzeCode(func, asmData, func);
+ //printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length, newIdent, measureSize(code), JSON.stringify(codeInfo.labels), JSON.stringify(codeInfo.breaks), JSON.stringify(codeInfo.continues)]);
+ var owned = { sp: 1 }; // sp is always owned, each has its own
+ keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
+ if (allCodeInfo.reads[v] === codeInfo.reads[v] && allCodeInfo.writes[v] === codeInfo.writes[v] && !(v in asmData.params)) {
+ owned[v] = 1;
+ }
+ });
var reps = [];
- for (var v in codeInfo.reads) {
- if (v != 'sp') {
- reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
- code.unshift(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]);
+ // wipe out control variable
+ reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]);
+ reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed
+ // add spills and reads before and after the call to the outlined code, and in the outlined code itself
+ keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
+ if (!(v in owned)) {
+ reps.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
}
- }
- reps.push(['stat', ['call', ['name', newIdent], [['name', 'sp']]]]);
+ });
+ reps.push(['stat', ['assign', true, ['name', 'sp'], makeAsmCoercion(['call', ['name', newIdent], [['name', 'sp']]], ASM_INT)]]);
for (var v in codeInfo.writes) {
- reps.push(['stat', ['assign', true, ['name', v], ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]]]]);
- code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(asmData, v) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
+ if (!(v in owned)) {
+ reps.push(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]);
+ }
}
// Generate new function
- if (codeInfo.hasReturn || codeInfo.hasBreak || codeInfo.hasContinue) {
+ if (codeInfo.hasReturn || codeInfo.hasReturnInt || codeInfo.hasReturnDouble || codeInfo.hasBreak || codeInfo.hasContinue) {
// we need to capture all control flow using a top-level labeled one-time loop in the outlined function
- code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]];
var breakCapturers = 0;
var continueCapturers = 0;
- traverse(code, function(node, type) {
+ traverse(['block', code], function(node, type) { // traverse on dummy block, so we get the toplevel statements
// replace all break/continue/returns with code to break out of the main one-time loop, and set the control data
- if (type == 'return') {
- var ret = ['break', 'OL'];
- if (!node[1]) {
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', CONTROL_RETURN_VOID]), ret];
- } else {
- var type = detectAsmCoercion(node[1], asmData);
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE]), ret];
- ret = ['seq', makeAssign(makeStackAccess(type, asmData.controlDataStackPos), node[1]), ret];
- }
- return ret;
- } else if (type == 'break') {
- var label = node[1] || 0;
- if (label == 'OL') return; // this was just added before us, it is new replacement code
- if (!label && breakCapturers > 0) return; // no label, and captured
- if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
- var ret = ['break', 'OL'];
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK]), ret];
- if (label) {
- assert(label in codeInfo.labels, label + ' in ' + keys(codeInfo.labels));
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
- }
- return ret;
- } else if (type == 'continue') {
- var label = node[1] || 0;
- if (!label && continueCapturers > 0) return; // no label, and captured
- if (label && (label in codeInfo.labels)) return; // label, and defined in this code, so captured
- var ret = ['break', 'OL'];
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE]), ret];
- if (label) {
- ret = ['seq', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos), ['num', codeInfo.labels[label]]), ret];
- }
- return ret;
- } else {
- if (type in BREAK_CAPTURERS) {
- breakCapturers++;
- }
- if (type in CONTINUE_CAPTURERS) {
- continueCapturers++;
+ if (type in BREAK_CAPTURERS) {
+ breakCapturers++;
+ }
+ if (type in CONTINUE_CAPTURERS) {
+ continueCapturers++;
+ }
+ var stats = node === code ? node : getStatements(node);
+ if (stats) {
+ for (var i = 0; i < stats.length; i++) {
+ var node = stats[i]; // step all over node and type here, for convenience
+ if (node[0] == 'stat') node = node[1];
+ var type = node[0];
+ var ret = null;
+ if (type == 'return') {
+ ret = [];
+ if (!node[1]) {
+ ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', CONTROL_RETURN_VOID])]);
+ } else {
+ var type = detectAsmCoercion(node[1], asmData);
+ ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', type == ASM_INT ? CONTROL_RETURN_INT : CONTROL_RETURN_DOUBLE])]);
+ ret.push(['stat', makeAssign(makeStackAccess(type, asmData.controlDataStackPos(outlineIndex)), node[1])]);
+ }
+ ret.push(['stat', ['break', 'OL']]);
+ } else if (type == 'break') {
+ var label = node[1] || 0;
+ if (label == 'OL') continue; // this was just added before us, it is new replacement code
+ if (!label && breakCapturers > 0) continue; // no label, and captured
+ if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured
+ ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_BREAK_LABEL : CONTROL_BREAK])]];
+ if (label) {
+ assert(label in codeInfo.breaks);
+ ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.breaks[label]])]);
+ }
+ ret.push(['stat', ['break', 'OL']]);
+ } else if (type == 'continue') {
+ var label = node[1] || 0;
+ if (!label && continueCapturers > 0) continue; // no label, and captured
+ if (label && (label in codeInfo.labels)) continue; // label, and defined in this code, so captured
+ ret = [['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', label ? CONTROL_CONTINUE_LABEL : CONTROL_CONTINUE])]];
+ if (label) {
+ assert(label in codeInfo.continues);
+ ret.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', codeInfo.continues[label]])]);
+ }
+ ret.push(['stat', ['break', 'OL']]);
+ }
+ if (ret) {
+ stats.splice.apply(stats, [i, 1].concat(ret));
+ i += ret.length-1;
+ }
}
}
}, function(node, type) {
@@ -3156,70 +3223,167 @@ function outline(ast) {
continueCapturers--;
}
});
- // read the control data at the callsite to the outlined function
+ code = [['label', 'OL', ['do', ['num', 0], ['block', code]]]]; // do this after processing, to not confuse breakCapturers etc.
+ // read the control data at the callsite to the outlined function, and clear the control values
+ reps.push(['stat', makeAssign(
+ ['name', 'tempValue'],
+ makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ASM_INT)
+ )]);
+ reps.push(['stat', makeAssign(
+ ['name', 'tempInt'],
+ makeAsmCoercion(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ASM_INT)
+ )]);
+ reps.push(['stat', makeAssign(
+ ['name', 'tempDouble'],
+ makeAsmCoercion(makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos(outlineIndex)), ASM_DOUBLE)
+ )]);
+ reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlStackPos(outlineIndex)), ['num', 0])]);
+ reps.push(['stat', makeAssign(makeStackAccess(ASM_INT, asmData.controlDataStackPos(outlineIndex)), ['num', 0])]); // XXX not really needed
+ // use the control data information
if (codeInfo.hasReturn) {
reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_VOID]),
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_VOID]),
[['stat', ['return']]]
));
+ }
+ if (codeInfo.hasReturnInt) {
reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_INT]),
- [['stat', ['return', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]]]
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_INT]),
+ [['stat', ['return', makeAsmCoercion(['name', 'tempInt'], ASM_INT)]]]
));
+ }
+ if (codeInfo.hasReturnDouble) {
reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_RETURN_DOUBLE]),
- [['stat', ['return', makeStackAccess(ASM_DOUBLE, asmData.controlDataStackPos)]]]
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_RETURN_DOUBLE]),
+ [['stat', ['return', makeAsmCoercion(['name', 'tempDouble'], ASM_DOUBLE)]]]
));
}
if (codeInfo.hasBreak) {
reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK]),
- ['stat', ['break']]
- ));
- reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_BREAK_LABEL]),
- ['stat', ['break', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX here and below, need a switch overall possible labels
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK]),
+ [['stat', ['break']]]
));
+ if (keys(codeInfo.breaks).length > 0) {
+ reps.push(makeIf(
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_BREAK_LABEL]),
+ [makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.breaks).map(function(key) {
+ var id = codeInfo.breaks[key];
+ return [['num', id], [['block', [['stat', ['break', key]]]]]];
+ }))]
+ ));
+ }
}
if (codeInfo.hasContinue) {
reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE]),
- ['stat', ['break']]
- ));
- reps.push(makeIf(
- makeComparison(makeStackAccess(ASM_INT, asmData.controlStackPos), '==', ['num', CONTROL_CONTINUE_LABEL]),
- ['stat', ['continue', makeStackAccess(ASM_INT, asmData.controlDataStackPos)]] // XXX
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE]),
+ [['stat', ['continue']]]
));
+ if (keys(codeInfo.continues).length > 0) {
+ reps.push(makeIf(
+ makeComparison(makeAsmCoercion(['name', 'tempValue'], ASM_INT), '==', ['num', CONTROL_CONTINUE_LABEL]),
+ [makeSwitch(makeAsmCoercion(['name', 'tempInt'], ASM_INT), keys(codeInfo.continues).map(function(key) {
+ var id = codeInfo.continues[key];
+ return [['num', id], [['block', [['stat', ['continue', key]]]]]];
+ }))]
+ ));
+ }
+ }
+ }
+ // add spills and unspills in outlined code outside the OL loop
+ keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
+ if (!(v in owned)) {
+ code.unshift(['stat', ['assign', true, ['name', v], makeAsmCoercion(['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], getAsmType(v, asmData))]]);
+ }
+ });
+ for (var v in codeInfo.writes) {
+ if (!(v in owned)) {
+ code.push(['stat', ['assign', true, ['sub', ['name', getAsmType(v, asmData) == ASM_INT ? 'HEAP32' : 'HEAPF32'], ['binary', '>>', ['binary', '+', ['name', 'sp'], ['num', asmData.stackPos[v]]], ['num', '2']]], ['name', v]]]);
}
}
+ // add final return of sp. the model is that we send sp as the single param, and get it back out
+ code.push(['stat', ['return', makeAsmCoercion(['name', 'sp'], ASM_INT)]]);
+ // finalize
var newFunc = ['defun', newIdent, ['sp'], code];
var newAsmData = { params: { sp: ASM_INT }, vars: {} };
for (var v in codeInfo.reads) {
- newAsmData.vars[v] = getAsmType(asmData, v);
+ if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
}
for (var v in codeInfo.writes) {
- newAsmData.vars[v] = getAsmType(asmData, v);
+ if (v != 'sp') newAsmData.vars[v] = getAsmType(v, asmData);
}
denormalizeAsm(newFunc, newAsmData);
+ // add outline call markers (we cannot do later outlinings that cut through an outlining call)
+ reps.unshift(['begin-outline-call', newIdent]);
+ reps.push(['end-outline-call', newIdent]);
// replace in stats
stats.splice.apply(stats, [start, end-start+1].concat(reps));
+ // final evaluation and processing
+ if (!extraInfo.allowCostlyOutlines && (measureSize(func) >= funcSize || measureSize(newFunc) >= funcSize)) {
+ // abort, this was pointless
+ stats.length = originalStats.length;
+ for (var i = 0; i < stats.length; i++) stats[i] = originalStats[i];
+ return [];
+ }
+ for (var v in owned) {
+ if (v != 'sp') delete asmData.vars[v]; // parent does not need these anymore
+ }
+ // if we just removed a final return from the original function, add one
+ var last = getStatements(func)[getStatements(func).length-1];
+ if (last[0] === 'stat') last = last[1];
+ if (last[0] !== 'return') {
+ if (allCodeInfo.hasReturnInt || allCodeInfo.hasReturnDouble) {
+ getStatements(func).push(['stat', ['return', makeAsmCoercion(['num', 0], allCodeInfo.hasReturnInt ? ASM_INT : ASM_DOUBLE)]]);
+ }
+ }
+ outliningParents[newIdent] = func[1];
return [newFunc];
}
function outlineStatements(func, asmData, stats, maxSize) {
level++;
- if (measureSize(stats) < sizeToOutline) return;
+ printErr('outlineStatements: ' + [func[1], level, measureSize(func)]);
+ var lastSize = measureSize(stats);
+ if (lastSize < sizeToOutline) { level--; return }
var ret = [];
var sizeSeen = 0;
var end = stats.length-1;
var i = stats.length;
- while (--i >= 0) {
+ var minIndex = stats == getStatements(func) ? getFirstIndexInNormalized(func, asmData) : 0;
+ var canRestart = false;
+ while (1) {
+ i--;
+ if (i < minIndex) {
+ // we might be done. but, if we have just outlined, do a further attempt from the beginning.
+ // (but only if the total costs are not extravagant)
+ var currSize = measureSize(stats);
+ var outlinedSize = measureSize(ret);
+ if (canRestart && currSize > 1.2*sizeToOutline && lastSize - currSize >= 0.75*sizeToOutline) {
+ printErr('restarting ' + func[1] + ' since ' + [currSize, outlinedSize, lastSize] + ' in level ' + level);
+ lastSize = currSize;
+ i = stats.length;
+ end = stats.length-1;
+ sizeSeen = 0;
+ canRestart = false;
+ continue;
+ } else {
+ break;
+ }
+ }
var stat = stats[i];
+ if (stat[0] === 'end-outline-call') {
+ // we cannot outline through an outline call, so include all of it
+ while (stats[i--][0] !== 'begin-outline-call') {
+ assert(i >= 0);
+ }
+ assert(i >= 0);
+ stat = stats[i];
+ }
var size = measureSize(stat);
//printErr(level + ' size ' + [i, size]);
if (size >= sizeToOutline) {
// this by itself is big enough to inline, recurse into it and find statements to split on
var subStatements = null;
+ var pre = ret.length;
traverse(stat, function(node, type) {
if (type == 'block') {
if (measureSize(node) >= sizeToOutline) {
@@ -3229,14 +3393,20 @@ function outline(ast) {
return null; // do not recurse into children, outlineStatements will do so if necessary
}
});
- sizeSeen = 0;
- continue;
+ if (ret.length > pre) {
+ // we outlined recursively, reset our state here
+ printErr('successful outline in recursion ' + func[1] + ' due to recursive in level ' + level);
+ end = i-1;
+ sizeSeen = 0;
+ canRestart = true;
+ continue;
+ }
}
sizeSeen += size;
- // If this is big enough to outline, but no too big (if very close to the size of the full function,
+ // If this is big enough to outline, but not too big (if very close to the size of the full function,
// outlining is pointless; remove stats from the end to try to achieve the good case), then outline.
// Also, try to reduce the size if it is much larger than the hoped-for size
- while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && i < end) {
+ while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && i < end && stats[i][0] !== 'end-outline-call') {
sizeSeen -= measureSize(stats[end]);
if (sizeSeen >= sizeToOutline) {
end--;
@@ -3247,8 +3417,11 @@ function outline(ast) {
}
if (sizeSeen >= sizeToOutline && sizeSeen <= maxSize) {
ret.push.apply(ret, doOutline(func, asmData, stats, i, end)); // outline [i, .. ,end] inclusive
+ printErr('performed outline on ' + func[1] + ' of ' + sizeSeen + ', func is now size ' + measureSize(func));
sizeSeen = 0;
end = i-1;
+ canRestart = true;
+ continue;
}
}
level--;
@@ -3274,29 +3447,78 @@ function outline(ast) {
var asmData = normalizeAsm(func);
var size = measureSize(func);
if (size >= sizeToOutline) {
+ printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + sizeToOutline + ')');
aggressiveVariableElimination(func, asmData);
analyzeFunction(func, asmData);
- var ret = outlineStatements(func, asmData, getStatements(func), 0.5*size);
- if (ret && ret.length > 0) newFuncs.push.apply(newFuncs, ret);
+ var stats = getStatements(func);
+ var ret = outlineStatements(func, asmData, stats, 0.9*size);
+ assert(level == 0);
+ if (ret && ret.length > 0) {
+ newFuncs.push.apply(newFuncs, ret);
+ // We have outlined. Add stack support
+ if ('sp' in asmData.vars) {
+ // find stack bump (STACKTOP = STACKTOP + X | 0) and add the extra space
+ var stackBumpNode = getStackBumpNode(stats);
+ if (stackBumpNode) stackBumpNode[3][2][3][1] = asmData.totalStackSize;
+ } else if (!('sp' in asmData.params)) { // if sp is a param, then we are an outlined function, no need to add stack support for us
+ // add sp variable and stack bump
+ var index = getFirstIndexInNormalized(func, asmData);
+ stats.splice(index, 0,
+ ['stat', makeAssign(['name', 'sp'], ['name', 'STACKTOP'])],
+ ['stat', makeAssign(['name', 'STACKTOP'], ['binary', '|', ['binary', '+', ['name', 'STACKTOP'], ['num', asmData.totalStackSize]], ['num', 0]])]
+ );
+ asmData.vars.sp = ASM_INT; // no need to add to vars, we are about to denormalize anyhow
+ // we added sp, so we must add stack popping
+ function makePop() {
+ return ['stat', makeAssign(['name', 'STACKTOP'], ['name', 'sp'])];
+ }
+ traverse(func, function(node, type) {
+ var stats = getStatements(node);
+ if (!stats) return;
+ for (var i = 0; i < stats.length; i++) {
+ var subNode = stats[i];
+ if (subNode[0] === 'stat') subNode = subNode[1];
+ if (subNode[0] == 'return') {
+ stats.splice(i, 0, makePop());
+ i++;
+ }
+ }
+ });
+ // pop the stack at the end if there is not a return
+ var last = stats[stats.length-1];
+ if (last[0] === 'stat') last = last[1];
+ if (last[0] !== 'return') {
+ stats.push(makePop());
+ }
+ }
+ }
+ printErr('... resulting size of ' + func[1] + ' is ' + measureSize(func));
}
denormalizeAsm(func, asmData);
});
+ funcs = null;
+
// TODO: control flow: route returns and breaks. outlined code should have all breaks/continues/returns break into the outermost scope,
// after setting a state variable, etc.
if (newFuncs.length > 0) {
- // We have outlined. Add stack support: header in which we allocate enough stack space TODO
- // If sp was not present before, add it and before each return, pop the stack. also a final pop if not ending with a return TODO
- // (none of this should be done in inner functions, of course, just the original)
-
// add new functions to the toplevel, or create a toplevel if there isn't one
ast[1].push.apply(ast[1], newFuncs);
- funcs = newFuncs;
- more = true;
+ // TODO: check if in some cases we do need to outline new functions
+ //funcs = newFuncs.filter(function(newFunc) {
+ // // recursively outline if we have a large new function that did not come at a high cost
+ // return measureSize(newFunc) > sizeToOutline && costs[newFunc[1]] < 0.1*sizeToOutline;
+ //});
+ //more = funcs.length > 0;
}
}
+
+ // clear out markers
+ traverse(ast, function(node, type) {
+ if (type === 'begin-outline-call' || type === 'end-outline-call') return emptyNode();
+ });
}
// Last pass utilities
@@ -3378,10 +3600,9 @@ var passes = {
unGlobalize: unGlobalize,
removeAssignsToUndefined: removeAssignsToUndefined,
//removeUnneededLabelSettings: removeUnneededLabelSettings,
- simplifyExpressionsPre: simplifyExpressionsPre,
+ simplifyExpressions: simplifyExpressions,
optimizeShiftsConservative: optimizeShiftsConservative,
optimizeShiftsAggressive: optimizeShiftsAggressive,
- simplifyExpressionsPost: simplifyExpressionsPost,
hoistMultiples: hoistMultiples,
loopOptimizer: loopOptimizer,
registerize: registerize,
diff --git a/tools/js_optimizer.py b/tools/js_optimizer.py
index a4e1ca6c..4e7d5474 100644
--- a/tools/js_optimizer.py
+++ b/tools/js_optimizer.py
@@ -102,16 +102,20 @@ start_asm_marker = '// EMSCRIPTEN_START_ASM\n'
end_asm_marker = '// EMSCRIPTEN_END_ASM\n'
def run_on_chunk(command):
- filename = command[2] # XXX hackish
- #print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read()
- 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 = temp_files.get(os.path.basename(filename) + '.jo.js').name
- f = open(filename, 'w')
- f.write(output)
- f.close()
- if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console.
- return filename
+ try:
+ filename = command[2] # XXX hackish
+ #print >> sys.stderr, 'running js optimizer command', ' '.join(command), '""""', open(filename).read()
+ 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 = temp_files.get(os.path.basename(filename) + '.jo.js').name
+ f = open(filename, 'w')
+ f.write(output)
+ f.close()
+ if DEBUG and not shared.WINDOWS: print >> sys.stderr, '.' # Skip debug progress indicator on Windows, since it doesn't buffer well with multiple threads printing to console.
+ return filename
+ except KeyboardInterrupt:
+ # avoid throwing keyboard interrupts from a child process
+ raise Exception()
def run_on_js(filename, passes, js_engine, jcache, source_map=False, extra_info=None):
if isinstance(jcache, bool) and jcache: jcache = shared.JCache
diff --git a/tools/shared.py b/tools/shared.py
index 0351a736..007c2ee8 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -726,7 +726,7 @@ class Building:
env['HOST_CXXFLAGS'] = "-W" #if set to nothing, CXXFLAGS is used, which we don't want
env['PKG_CONFIG_LIBDIR'] = path_from_root('system', 'local', 'lib', 'pkgconfig') + os.path.pathsep + path_from_root('system', 'lib', 'pkgconfig')
env['PKG_CONFIG_PATH'] = os.environ.get ('EM_PKG_CONFIG_PATH') or ''
- env['EMSCRIPTEN'] = '1'
+ env['EMSCRIPTEN'] = path_from_root()
return env
@staticmethod
diff --git a/tools/test-js-optimizer-asm-outline-output.js b/tools/test-js-optimizer-asm-outline-output.js
deleted file mode 100644
index c54fc346..00000000
--- a/tools/test-js-optimizer-asm-outline-output.js
+++ /dev/null
@@ -1,570 +0,0 @@
-function linear() {
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
- cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
-}
-function _free($mem) {
- $mem = $mem | 0;
- var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $37 = 0, $40 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $80 = 0, $95 = 0, $100 = 0, $RP_0 = 0, $R_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $R_1 = 0, $120 = 0, $151 = 0, $164 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, $204 = 0, $220 = 0, $227 = 0, $233 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $278 = 0, $294 = 0, $299 = 0, $RP9_0 = 0, $R7_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $R7_1 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $404 = 0, $_pre_phi = 0, $F16_0 = 0, $414 = 0, $415 = 0, $428 = 0, $436 = 0, $I18_0 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $T_0 = 0, $K19_0 = 0, $472 = 0, $473 = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0, label = 0, sp = 0;
- sp = STACKTOP;
- if (($mem | 0) == 0) {
- STACKTOP = sp;
- return;
- }
- $5 = HEAP32[24] | 0;
- if (($mem - 8 | 0) >>> 0 < $5 >>> 0) {
- _abort();
- }
- $10 = HEAP32[$mem - 4 >> 2] | 0;
- if (($10 & 3 | 0) == 1) {
- _abort();
- }
- $16 = $mem + (($10 & -8) - 8) | 0;
- L621 : do {
- if (($10 & 1 | 0) == 0) {
- $21 = HEAP32[($mem - 8 | 0) >> 2] | 0;
- if (($10 & 3 | 0) == 0) {
- return;
- }
- $25 = $mem + (-8 - $21 | 0) | 0;
- $26 = $21 + ($10 & -8) | 0;
- if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) {
- _abort();
- }
- if (($25 | 0) == (HEAP32[25] | 0)) {
- if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) {
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- HEAP32[22] = $26;
- HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2;
- HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1;
- HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26;
- return;
- }
- if ($21 >>> 0 < 256) {
- $37 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0;
- $40 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0;
- do {
- if (($37 | 0) != (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) {
- if ($37 >>> 0 < $5 >>> 0) {
- _abort();
- }
- if ((HEAP32[$37 + 12 >> 2] | 0) == ($25 | 0)) {
- break;
- }
- _abort();
- }
- } while (0);
- if (($40 | 0) == ($37 | 0)) {
- HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1);
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- do {
- if (($40 | 0) == (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) {
- $_pre_phi307 = $40 + 8 | 0;
- } else {
- if ($40 >>> 0 < $5 >>> 0) {
- _abort();
- }
- if ((HEAP32[($40 + 8 | 0) >> 2] | 0) == ($25 | 0)) {
- $_pre_phi307 = $40 + 8 | 0;
- break;
- }
- _abort();
- }
- } while (0);
- HEAP32[$37 + 12 >> 2] = $40;
- HEAP32[$_pre_phi307 >> 2] = $37;
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- $69 = $mem + (-8 - $21 | 0) | 0;
- $72 = HEAP32[$mem + ((-8 - $21 | 0) + 24) >> 2] | 0;
- $75 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0;
- do {
- if (($75 | 0) == ($69 | 0)) {
- $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0;
- if (($95 | 0) == 0) {
- $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0;
- if (($100 | 0) == 0) {
- $R_1 = 0;
- break;
- } else {
- $R_0 = $100;
- $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0;
- }
- } else {
- $R_0 = $95;
- $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0;
- }
- while (1) {
- $102 = $R_0 + 20 | 0;
- $103 = HEAP32[$102 >> 2] | 0;
- if (($103 | 0) != 0) {
- $R_0 = $103;
- $RP_0 = $102;
- continue;
- }
- $106 = $R_0 + 16 | 0;
- $107 = HEAP32[$106 >> 2] | 0;
- if (($107 | 0) == 0) {
- break;
- } else {
- $R_0 = $107;
- $RP_0 = $106;
- }
- }
- if ($RP_0 >>> 0 < $5 >>> 0) {
- _abort();
- } else {
- HEAP32[$RP_0 >> 2] = 0;
- $R_1 = $R_0;
- break;
- }
- } else {
- $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0;
- if ($80 >>> 0 < $5 >>> 0) {
- _abort();
- }
- if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) {
- _abort();
- }
- if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) {
- HEAP32[($80 + 12 | 0) >> 2] = $75;
- HEAP32[($75 + 8 | 0) >> 2] = $80;
- $R_1 = $75;
- break;
- } else {
- _abort();
- }
- }
- } while (0);
- if (($72 | 0) == 0) {
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0;
- do {
- if (($69 | 0) == (HEAP32[$120 >> 2] | 0)) {
- HEAP32[$120 >> 2] = $R_1;
- if (($R_1 | 0) != 0) {
- break;
- }
- HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1);
- $p_0 = $25;
- $psize_0 = $26;
- break L621;
- } else {
- if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- if ((HEAP32[($72 + 16 | 0) >> 2] | 0) == ($69 | 0)) {
- HEAP32[($72 + 16 | 0) >> 2] = $R_1;
- } else {
- HEAP32[$72 + 20 >> 2] = $R_1;
- }
- if (($R_1 | 0) == 0) {
- $p_0 = $25;
- $psize_0 = $26;
- break L621;
- }
- }
- } while (0);
- if ($R_1 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- HEAP32[$R_1 + 24 >> 2] = $72;
- $151 = HEAP32[$mem + ((-8 - $21 | 0) + 16) >> 2] | 0;
- do {
- if (($151 | 0) != 0) {
- if ($151 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$R_1 + 16 >> 2] = $151;
- HEAP32[$151 + 24 >> 2] = $R_1;
- break;
- }
- }
- } while (0);
- $164 = HEAP32[$mem + ((-8 - $21 | 0) + 20) >> 2] | 0;
- if (($164 | 0) == 0) {
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$R_1 + 20 >> 2] = $164;
- HEAP32[$164 + 24 >> 2] = $R_1;
- $p_0 = $25;
- $psize_0 = $26;
- break;
- }
- } else {
- $p_0 = $mem - 8 | 0;
- $psize_0 = $10 & -8;
- }
- } while (0);
- $189 = $p_0;
- if ($189 >>> 0 >= ($mem + (($10 & -8) - 8) | 0) >>> 0) {
- _abort();
- }
- $194 = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] | 0;
- if (($194 & 1 | 0) == 0) {
- _abort();
- }
- do {
- if (($194 & 2 | 0) == 0) {
- if (($16 | 0) == (HEAP32[26] | 0)) {
- $204 = (HEAP32[23] | 0) + $psize_0 | 0;
- HEAP32[23] = $204;
- HEAP32[26] = $p_0;
- HEAP32[$p_0 + 4 >> 2] = $204 | 1;
- if (($p_0 | 0) == (HEAP32[25] | 0)) {
- HEAP32[25] = 0;
- HEAP32[22] = 0;
- }
- if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) {
- return;
- }
- _sys_trim(0) | 0;
- return;
- }
- if (($16 | 0) == (HEAP32[25] | 0)) {
- $220 = (HEAP32[22] | 0) + $psize_0 | 0;
- HEAP32[22] = $220;
- HEAP32[25] = $p_0;
- HEAP32[$p_0 + 4 >> 2] = $220 | 1;
- HEAP32[$189 + $220 >> 2] = $220;
- return;
- }
- $227 = ($194 & -8) + $psize_0 | 0;
- L726 : do {
- if ($194 >>> 0 < 256) {
- $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0;
- $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0;
- do {
- if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) {
- if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) {
- break;
- }
- _abort();
- }
- } while (0);
- if (($236 | 0) == ($233 | 0)) {
- HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1);
- break;
- }
- do {
- if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) {
- $_pre_phi305 = $236 + 8 | 0;
- } else {
- if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) {
- $_pre_phi305 = $236 + 8 | 0;
- break;
- }
- _abort();
- }
- } while (0);
- HEAP32[$233 + 12 >> 2] = $236;
- HEAP32[$_pre_phi305 >> 2] = $233;
- } else {
- $267 = $mem + (($10 & -8) - 8) | 0;
- $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0;
- $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0;
- do {
- if (($273 | 0) == ($267 | 0)) {
- $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0;
- if (($294 | 0) == 0) {
- $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0;
- if (($299 | 0) == 0) {
- $R7_1 = 0;
- break;
- } else {
- $R7_0 = $299;
- $RP9_0 = $mem + (($10 & -8) + 8) | 0;
- }
- } else {
- $R7_0 = $294;
- $RP9_0 = $mem + (($10 & -8) + 12) | 0;
- }
- while (1) {
- $301 = $R7_0 + 20 | 0;
- $302 = HEAP32[$301 >> 2] | 0;
- if (($302 | 0) != 0) {
- $R7_0 = $302;
- $RP9_0 = $301;
- continue;
- }
- $305 = $R7_0 + 16 | 0;
- $306 = HEAP32[$305 >> 2] | 0;
- if (($306 | 0) == 0) {
- break;
- } else {
- $R7_0 = $306;
- $RP9_0 = $305;
- }
- }
- if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$RP9_0 >> 2] = 0;
- $R7_1 = $R7_0;
- break;
- }
- } else {
- $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0;
- if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) {
- _abort();
- }
- if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) {
- HEAP32[($278 + 12 | 0) >> 2] = $273;
- HEAP32[($273 + 8 | 0) >> 2] = $278;
- $R7_1 = $273;
- break;
- } else {
- _abort();
- }
- }
- } while (0);
- if (($270 | 0) == 0) {
- break;
- }
- $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0;
- do {
- if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) {
- HEAP32[$320 >> 2] = $R7_1;
- if (($R7_1 | 0) != 0) {
- break;
- }
- HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1);
- break L726;
- } else {
- if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) {
- HEAP32[($270 + 16 | 0) >> 2] = $R7_1;
- } else {
- HEAP32[$270 + 20 >> 2] = $R7_1;
- }
- if (($R7_1 | 0) == 0) {
- break L726;
- }
- }
- } while (0);
- if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- }
- HEAP32[$R7_1 + 24 >> 2] = $270;
- $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0;
- do {
- if (($351 | 0) != 0) {
- if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$R7_1 + 16 >> 2] = $351;
- HEAP32[$351 + 24 >> 2] = $R7_1;
- break;
- }
- }
- } while (0);
- $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0;
- if (($364 | 0) == 0) {
- break;
- }
- if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$R7_1 + 20 >> 2] = $364;
- HEAP32[$364 + 24 >> 2] = $R7_1;
- break;
- }
- }
- } while (0);
- HEAP32[$p_0 + 4 >> 2] = $227 | 1;
- HEAP32[$189 + $227 >> 2] = $227;
- if (($p_0 | 0) != (HEAP32[25] | 0)) {
- $psize_1 = $227;
- break;
- }
- HEAP32[22] = $227;
- return;
- } else {
- HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2;
- HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1;
- HEAP32[$189 + $psize_0 >> 2] = $psize_0;
- $psize_1 = $psize_0;
- }
- } while (0);
- $390 = $psize_1 >>> 3;
- if ($psize_1 >>> 0 < 256) {
- $396 = HEAP32[20] | 0;
- do {
- if (($396 & 1 << $390 | 0) == 0) {
- HEAP32[20] = $396 | 1 << $390;
- $F16_0 = 120 + ($390 << 1 << 2) | 0;
- $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0;
- } else {
- $404 = HEAP32[(120 + (($390 << 1) + 2 << 2) | 0) >> 2] | 0;
- if ($404 >>> 0 >= (HEAP32[24] | 0) >>> 0) {
- $F16_0 = $404;
- $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0;
- break;
- }
- _abort();
- }
- } while (0);
- HEAP32[$_pre_phi >> 2] = $p_0;
- HEAP32[$F16_0 + 12 >> 2] = $p_0;
- HEAP32[$p_0 + 8 >> 2] = $F16_0;
- HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0;
- return;
- }
- $414 = $p_0;
- $415 = $psize_1 >>> 8;
- do {
- if (($415 | 0) == 0) {
- $I18_0 = 0;
- } else {
- if ($psize_1 >>> 0 > 16777215) {
- $I18_0 = 31;
- break;
- }
- $428 = $415 << (($415 + 1048320 | 0) >>> 16 & 8) << ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4);
- $436 = 14 - ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4 | ($415 + 1048320 | 0) >>> 16 & 8 | ($428 + 245760 | 0) >>> 16 & 2) + ($428 << (($428 + 245760 | 0) >>> 16 & 2) >>> 15) | 0;
- $I18_0 = $psize_1 >>> (($436 + 7 | 0) >>> 0) & 1 | $436 << 1;
- }
- } while (0);
- $443 = 384 + ($I18_0 << 2) | 0;
- HEAP32[$p_0 + 28 >> 2] = $I18_0;
- HEAP32[$p_0 + 20 >> 2] = 0;
- HEAP32[$p_0 + 16 >> 2] = 0;
- $447 = HEAP32[21] | 0;
- $448 = 1 << $I18_0;
- do {
- if (($447 & $448 | 0) == 0) {
- HEAP32[21] = $447 | $448;
- HEAP32[$443 >> 2] = $414;
- HEAP32[$p_0 + 24 >> 2] = $443;
- HEAP32[$p_0 + 12 >> 2] = $p_0;
- HEAP32[$p_0 + 8 >> 2] = $p_0;
- } else {
- if (($I18_0 | 0) == 31) {
- $463 = 0;
- } else {
- $463 = 25 - ($I18_0 >>> 1) | 0;
- }
- $K19_0 = $psize_1 << $463;
- $T_0 = HEAP32[$443 >> 2] | 0;
- while (1) {
- if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) {
- break;
- }
- $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0;
- $473 = HEAP32[$472 >> 2] | 0;
- if (($473 | 0) == 0) {
- label = 569;
- break;
- } else {
- $K19_0 = $K19_0 << 1;
- $T_0 = $473;
- }
- }
- if ((label | 0) == 569) {
- if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) {
- _abort();
- } else {
- HEAP32[$472 >> 2] = $414;
- HEAP32[$p_0 + 24 >> 2] = $T_0;
- HEAP32[$p_0 + 12 >> 2] = $p_0;
- HEAP32[$p_0 + 8 >> 2] = $p_0;
- break;
- }
- }
- $486 = $T_0 + 8 | 0;
- $487 = HEAP32[$486 >> 2] | 0;
- $489 = HEAP32[24] | 0;
- if ($T_0 >>> 0 < $489 >>> 0) {
- _abort();
- }
- if ($487 >>> 0 < $489 >>> 0) {
- _abort();
- } else {
- HEAP32[$487 + 12 >> 2] = $414;
- HEAP32[$486 >> 2] = $414;
- HEAP32[$p_0 + 8 >> 2] = $487;
- HEAP32[$p_0 + 12 >> 2] = $T_0;
- HEAP32[$p_0 + 24 >> 2] = 0;
- break;
- }
- }
- } while (0);
- $501 = (HEAP32[28] | 0) - 1 | 0;
- HEAP32[28] = $501;
- if (($501 | 0) == 0) {
- $sp_0_in_i = 536;
- } else {
- return;
- }
- while (1) {
- $sp_0_i = HEAP32[$sp_0_in_i >> 2] | 0;
- if (($sp_0_i | 0) == 0) {
- break;
- } else {
- $sp_0_in_i = $sp_0_i + 8 | 0;
- }
- }
- HEAP32[28] = -1;
- STACKTOP = sp;
- return;
-}
-
diff --git a/tools/test-js-optimizer-asm-outline1-output.js b/tools/test-js-optimizer-asm-outline1-output.js
new file mode 100644
index 00000000..8d1071a9
--- /dev/null
+++ b/tools/test-js-optimizer-asm-outline1-output.js
@@ -0,0 +1,566 @@
+function lin() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = lin$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = lin$0(sp) | 0;
+ STACKTOP = sp;
+}
+function lin2() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = lin2$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = lin2$0(sp) | 0;
+ }
+ STACKTOP = sp;
+}
+function lin3() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = lin3$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = lin3$0(sp) | 0;
+ tempValue = HEAP32[sp + 8 >> 2] | 0;
+ tempInt = HEAP32[sp + 12 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 12 >> 2];
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ if ((tempValue | 0) == 6) {
+ STACKTOP = sp;
+ return tempInt | 0;
+ }
+ }
+ STACKTOP = sp;
+ return 20;
+}
+function lin4() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = lin4$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = lin4$0(sp) | 0;
+ tempValue = HEAP32[sp + 8 >> 2] | 0;
+ tempInt = HEAP32[sp + 12 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 12 >> 2];
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ if ((tempValue | 0) == 1) {
+ break;
+ }
+ }
+ STACKTOP = sp;
+ return 20;
+}
+function lin5() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = lin5$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = lin5$0(sp) | 0;
+ tempValue = HEAP32[sp + 8 >> 2] | 0;
+ tempInt = HEAP32[sp + 12 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 12 >> 2];
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ if ((tempValue | 0) == 3) {
+ continue;
+ }
+ }
+ STACKTOP = sp;
+ return 20;
+}
+function mix() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 88 | 0;
+ main : while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = mix$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = mix$0(sp) | 0;
+ tempValue = HEAP32[sp + 8 >> 2] | 0;
+ tempInt = HEAP32[sp + 12 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 12 >> 2];
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ if ((tempValue | 0) == 1) {
+ break;
+ }
+ if ((tempValue | 0) == 2) {
+ switch (tempInt | 0) {
+ case 2:
+ {
+ break main;
+ }
+ }
+ }
+ if ((tempValue | 0) == 3) {
+ continue;
+ }
+ if ((tempValue | 0) == 4) {
+ switch (tempInt | 0) {
+ case 3:
+ {
+ continue main;
+ }
+ }
+ }
+ }
+ STACKTOP = sp;
+ return 20;
+}
+function vars(x, y) {
+ x = x | 0;
+ y = +y;
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 88 | 0;
+ HEAP32[sp + 32 >> 2] = 0;
+ HEAP32[sp + 36 >> 2] = 0;
+ HEAP32[sp + 8 >> 2] = x;
+ HEAPF32[sp + 16 >> 2] = y;
+ sp = vars$1(sp) | 0;
+ HEAP32[sp + 24 >> 2] = 0;
+ HEAP32[sp + 28 >> 2] = 0;
+ HEAP32[sp + 8 >> 2] = x;
+ HEAPF32[sp + 16 >> 2] = y;
+ sp = vars$0(sp) | 0;
+ STACKTOP = sp;
+}
+function vars2(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 88 | 0;
+ a = x + y;
+ b = y * x;
+ a = c(1 + a);
+ b = c(2 + b);
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ HEAP32[sp + 24 >> 2] = a;
+ HEAPF32[sp + 32 >> 2] = b;
+ sp = vars2$0(sp) | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ b = +HEAPF32[sp + 32 >> 2];
+ STACKTOP = sp;
+}
+function vars3(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 96 | 0;
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ HEAP32[sp + 24 >> 2] = a;
+ HEAP32[sp + 8 >> 2] = x;
+ HEAPF32[sp + 16 >> 2] = y;
+ sp = vars3$1(sp) | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ HEAP32[sp + 32 >> 2] = 0;
+ HEAP32[sp + 36 >> 2] = 0;
+ HEAP32[sp + 24 >> 2] = a;
+ HEAPF32[sp + 16 >> 2] = y;
+ HEAP32[sp + 8 >> 2] = x;
+ sp = vars3$0(sp) | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ STACKTOP = sp;
+}
+function vars4(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 104 | 0;
+ a = x + y;
+ HEAP32[sp + 48 >> 2] = 0;
+ HEAP32[sp + 52 >> 2] = 0;
+ HEAPF32[sp + 16 >> 2] = y;
+ HEAP32[sp + 8 >> 2] = x;
+ HEAP32[sp + 24 >> 2] = a;
+ HEAPF32[sp + 32 >> 2] = b;
+ sp = vars4$1(sp) | 0;
+ b = +HEAPF32[sp + 32 >> 2];
+ a = HEAP32[sp + 24 >> 2] | 0;
+ HEAP32[sp + 40 >> 2] = 0;
+ HEAP32[sp + 44 >> 2] = 0;
+ HEAP32[sp + 24 >> 2] = a;
+ HEAP32[sp + 8 >> 2] = x;
+ HEAPF32[sp + 32 >> 2] = b;
+ sp = vars4$0(sp) | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ b = +HEAPF32[sp + 32 >> 2];
+ STACKTOP = sp;
+}
+function vars_w_stack(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 144 | 0;
+ a = x + y;
+ HEAP32[sp + 72 >> 2] = 0;
+ HEAP32[sp + 76 >> 2] = 0;
+ HEAPF32[sp + 32 >> 2] = y;
+ HEAP32[sp + 24 >> 2] = x;
+ HEAP32[sp + 40 >> 2] = a;
+ HEAPF32[sp + 48 >> 2] = b;
+ sp = vars_w_stack$1(sp) | 0;
+ b = +HEAPF32[sp + 48 >> 2];
+ a = HEAP32[sp + 40 >> 2] | 0;
+ HEAP32[sp + 64 >> 2] = 0;
+ HEAP32[sp + 68 >> 2] = 0;
+ HEAP32[sp + 40 >> 2] = a;
+ HEAPF32[sp + 48 >> 2] = b;
+ sp = vars_w_stack$0(sp) | 0;
+ a = HEAP32[sp + 40 >> 2] | 0;
+ b = +HEAPF32[sp + 48 >> 2];
+}
+function lin$0(sp) {
+ sp = sp | 0;
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ return sp | 0;
+}
+function lin$1(sp) {
+ sp = sp | 0;
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ return sp | 0;
+}
+function lin2$0(sp) {
+ sp = sp | 0;
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ return sp | 0;
+}
+function lin2$1(sp) {
+ sp = sp | 0;
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ return sp | 0;
+}
+function lin3$0(sp) {
+ sp = sp | 0;
+ OL : do {
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ HEAP32[sp + 8 >> 2] = 6;
+ HEAP32[sp + 12 >> 2] = 10;
+ break OL;
+ } while (0);
+ return sp | 0;
+}
+function lin3$1(sp) {
+ sp = sp | 0;
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ return sp | 0;
+}
+function lin4$0(sp) {
+ sp = sp | 0;
+ OL : do {
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ HEAP32[sp + 8 >> 2] = 1;
+ break OL;
+ } while (0);
+ return sp | 0;
+}
+function lin4$1(sp) {
+ sp = sp | 0;
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ return sp | 0;
+}
+function lin5$0(sp) {
+ sp = sp | 0;
+ OL : do {
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ HEAP32[sp + 8 >> 2] = 3;
+ break OL;
+ } while (0);
+ return sp | 0;
+}
+function lin5$1(sp) {
+ sp = sp | 0;
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ return sp | 0;
+}
+function mix$0(sp) {
+ sp = sp | 0;
+ OL : do {
+ c(16);
+ c(17);
+ HEAP32[sp + 8 >> 2] = 2;
+ HEAP32[sp + 12 >> 2] = 2;
+ break OL;
+ c(18);
+ HEAP32[sp + 8 >> 2] = 1;
+ break OL;
+ while (1) {
+ break;
+ }
+ inner : while (1) {
+ break inner;
+ }
+ c(19);
+ HEAP32[sp + 8 >> 2] = 3;
+ break OL;
+ c(20);
+ HEAP32[sp + 8 >> 2] = 4;
+ HEAP32[sp + 12 >> 2] = 3;
+ break OL;
+ } while (0);
+ return sp | 0;
+}
+function mix$1(sp) {
+ sp = sp | 0;
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ return sp | 0;
+}
+function vars$0(sp) {
+ sp = sp | 0;
+ var x = 0, y = +0;
+ y = +HEAPF32[sp + 16 >> 2];
+ x = HEAP32[sp + 8 >> 2] | 0;
+ c(5 + (x + y));
+ c(6 + y * x);
+ c(7 + (x + y));
+ c(8 + y * x);
+ return sp | 0;
+}
+function vars$1(sp) {
+ sp = sp | 0;
+ var x = 0, y = +0;
+ y = +HEAPF32[sp + 16 >> 2];
+ x = HEAP32[sp + 8 >> 2] | 0;
+ c(1 + (x + y));
+ c(2 + y * x);
+ c(3 + (x + y));
+ c(4 + y * x);
+ return sp | 0;
+}
+function vars2$0(sp) {
+ sp = sp | 0;
+ var a = 0, b = +0;
+ b = +HEAPF32[sp + 32 >> 2];
+ a = HEAP32[sp + 24 >> 2] | 0;
+ a = c(3 + a);
+ b = c(4 + b);
+ a = c(5 + a);
+ b = c(6 + b);
+ HEAP32[sp + 24 >> 2] = a;
+ HEAPF32[sp + 32 >> 2] = b;
+ return sp | 0;
+}
+function vars3$0(sp) {
+ sp = sp | 0;
+ var a = 0, y = +0, x = 0;
+ x = HEAP32[sp + 8 >> 2] | 0;
+ y = +HEAPF32[sp + 16 >> 2];
+ a = HEAP32[sp + 24 >> 2] | 0;
+ a = c(4 + y * x);
+ a = c(5 + a);
+ a = c(6 + y * x);
+ a = c(7 + a);
+ HEAP32[sp + 24 >> 2] = a;
+ return sp | 0;
+}
+function vars3$1(sp) {
+ sp = sp | 0;
+ var a = 0, x = 0, y = +0;
+ y = +HEAPF32[sp + 16 >> 2];
+ x = HEAP32[sp + 8 >> 2] | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ a = x + y;
+ a = c(1 + a);
+ a = c(2 + y * x);
+ a = c(3 + a);
+ HEAP32[sp + 24 >> 2] = a;
+ return sp | 0;
+}
+function vars4$0(sp) {
+ sp = sp | 0;
+ var a = 0, x = 0, b = +0;
+ b = +HEAPF32[sp + 32 >> 2];
+ x = HEAP32[sp + 8 >> 2] | 0;
+ a = HEAP32[sp + 24 >> 2] | 0;
+ a = c(4 + a);
+ a = c(5 + a);
+ a = c(6 + a);
+ b = c(7 + a + x);
+ HEAP32[sp + 24 >> 2] = a;
+ HEAPF32[sp + 32 >> 2] = b;
+ return sp | 0;
+}
+function vars4$1(sp) {
+ sp = sp | 0;
+ var y = +0, x = 0, a = 0, b = +0;
+ b = +HEAPF32[sp + 32 >> 2];
+ a = HEAP32[sp + 24 >> 2] | 0;
+ x = HEAP32[sp + 8 >> 2] | 0;
+ y = +HEAPF32[sp + 16 >> 2];
+ b = y * x;
+ a = c(1 + a);
+ a = c(2 + a);
+ a = c(3 + a);
+ HEAPF32[sp + 32 >> 2] = b;
+ HEAP32[sp + 24 >> 2] = a;
+ return sp | 0;
+}
+function vars_w_stack$0(sp) {
+ sp = sp | 0;
+ var a = 0, b = +0;
+ b = +HEAPF32[sp + 48 >> 2];
+ a = HEAP32[sp + 40 >> 2] | 0;
+ a = c(4 + a);
+ a = c(5 + a);
+ a = c(6 + a);
+ b = c(7 + a);
+ STACKTOP = sp;
+ HEAP32[sp + 40 >> 2] = a;
+ HEAPF32[sp + 48 >> 2] = b;
+ return sp | 0;
+}
+function vars_w_stack$1(sp) {
+ sp = sp | 0;
+ var y = +0, x = 0, a = 0, b = +0;
+ b = +HEAPF32[sp + 48 >> 2];
+ a = HEAP32[sp + 40 >> 2] | 0;
+ x = HEAP32[sp + 24 >> 2] | 0;
+ y = +HEAPF32[sp + 32 >> 2];
+ b = y * x;
+ a = c(1 + a);
+ a = c(2 + a);
+ a = c(3 + a);
+ HEAPF32[sp + 48 >> 2] = b;
+ HEAP32[sp + 40 >> 2] = a;
+ return sp | 0;
+}
+
diff --git a/tools/test-js-optimizer-asm-outline1.js b/tools/test-js-optimizer-asm-outline1.js
new file mode 100644
index 00000000..40026439
--- /dev/null
+++ b/tools/test-js-optimizer-asm-outline1.js
@@ -0,0 +1,234 @@
+function lin() {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+}
+function lin2() {
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ }
+}
+function lin3() {
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ return 10;
+ }
+ return 20;
+}
+function lin4() {
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ break;
+ }
+ return 20;
+}
+function lin5() {
+ while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ c(18);
+ c(19);
+ c(20);
+ continue;
+ }
+ return 20;
+}
+function mix() {
+ main: while (1) {
+ c(1);
+ c(2);
+ c(3);
+ c(4);
+ c(5);
+ c(6);
+ c(7);
+ c(8);
+ c(9);
+ c(10);
+ c(11);
+ c(12);
+ c(13);
+ c(14);
+ c(15);
+ c(16);
+ c(17);
+ break main;
+ c(18);
+ break;
+ while (1) {
+ break; // no need to forward
+ }
+ inner: while (1) {
+ break inner; // no need to forward
+ }
+ c(19);
+ continue;
+ c(20);
+ continue main;
+ }
+ return 20;
+}
+function vars(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0;
+ a = x+y;
+ b = y*x;
+ c(1+a);
+ c(2+b);
+ c(3+a);
+ c(4+b);
+ c(5+a);
+ c(6+b);
+ c(7+a);
+ c(8+b);
+}
+function vars2(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0;
+ a = x+y;
+ b = y*x;
+ a = c(1+a);
+ b = c(2+b);
+ a = c(3+a);
+ b = c(4+b);
+ a = c(5+a);
+ b = c(6+b);
+}
+function vars3(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0;
+ a = x+y;
+ b = y*x;
+ a = c(1+a);
+ a = c(2+b);
+ a = c(3+a);
+ a = c(4+b);
+ a = c(5+a);
+ a = c(6+b);
+ a = c(7+a);
+}
+function vars4(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0;
+ a = x+y;
+ b = y*x;
+ a = c(1+a);
+ a = c(2+a);
+ a = c(3+a);
+ a = c(4+a);
+ a = c(5+a);
+ a = c(6+a);
+ b = c(7+a+x);
+}
+function vars_w_stack(x, y) {
+ x = x | 0;
+ y = +y;
+ var a = 0, b = +0, sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 16 | 0;
+ a = x+y;
+ b = y*x;
+ a = c(1+a);
+ a = c(2+a);
+ a = c(3+a);
+ a = c(4+a);
+ a = c(5+a);
+ a = c(6+a);
+ b = c(7+a);
+ STACKTOP = sp;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS
+// EXTRA_INFO: { "sizeToOutline": 30, "allowCostlyOutlines": 1 }
diff --git a/tools/test-js-optimizer-asm-outline2-output.js b/tools/test-js-optimizer-asm-outline2-output.js
new file mode 100644
index 00000000..31bb7bf9
--- /dev/null
+++ b/tools/test-js-optimizer-asm-outline2-output.js
@@ -0,0 +1,728 @@
+function linear() {
+ var sp = 0;
+ sp = STACKTOP;
+ STACKTOP = STACKTOP + 72 | 0;
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ HEAP32[sp + 16 >> 2] = 0;
+ HEAP32[sp + 20 >> 2] = 0;
+ sp = linear$1(sp) | 0;
+ HEAP32[sp + 8 >> 2] = 0;
+ HEAP32[sp + 12 >> 2] = 0;
+ sp = linear$0(sp) | 0;
+ STACKTOP = sp;
+}
+function _free($mem) {
+ $mem = $mem | 0;
+ var $5 = 0, $10 = 0, $16 = 0, $21 = 0, $25 = 0, $26 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $194 = 0, sp = 0;
+ sp = STACKTOP;
+ if (($mem | 0) == 0) {
+ STACKTOP = sp;
+ return;
+ }
+ $5 = HEAP32[24] | 0;
+ if (($mem - 8 | 0) >>> 0 < $5 >>> 0) {
+ _abort();
+ }
+ $10 = HEAP32[$mem - 4 >> 2] | 0;
+ if (($10 & 3 | 0) == 1) {
+ _abort();
+ }
+ $16 = $mem + (($10 & -8) - 8) | 0;
+ L621 : do {
+ if (($10 & 1 | 0) == 0) {
+ $21 = HEAP32[($mem - 8 | 0) >> 2] | 0;
+ if (($10 & 3 | 0) == 0) {
+ return;
+ }
+ $25 = $mem + (-8 - $21 | 0) | 0;
+ $26 = $21 + ($10 & -8) | 0;
+ if (($mem + (-8 - $21 | 0) | 0) >>> 0 < $5 >>> 0) {
+ _abort();
+ }
+ HEAP32[sp + 648 >> 2] = 0;
+ HEAP32[sp + 652 >> 2] = 0;
+ HEAP32[sp + 48 >> 2] = $25;
+ HEAP32[sp + 8 >> 2] = $mem;
+ HEAP32[sp + 24 >> 2] = $10;
+ HEAP32[sp + 56 >> 2] = $26;
+ HEAP32[sp + 40 >> 2] = $21;
+ HEAP32[sp + 16 >> 2] = $5;
+ HEAP32[sp + 224 >> 2] = $p_0;
+ HEAP32[sp + 216 >> 2] = $psize_0;
+ sp = _free$1(sp) | 0;
+ $p_0 = HEAP32[sp + 224 >> 2] | 0;
+ $psize_0 = HEAP32[sp + 216 >> 2] | 0;
+ tempValue = HEAP32[sp + 648 >> 2] | 0;
+ tempInt = HEAP32[sp + 652 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 652 >> 2];
+ HEAP32[sp + 648 >> 2] = 0;
+ HEAP32[sp + 652 >> 2] = 0;
+ if ((tempValue | 0) == 5) {
+ return;
+ }
+ if ((tempValue | 0) == 1) {
+ break;
+ }
+ if ((tempValue | 0) == 2) {
+ switch (tempInt | 0) {
+ case 2:
+ {
+ break L621;
+ }
+ }
+ }
+ } else {
+ $p_0 = $mem - 8 | 0;
+ $psize_0 = $10 & -8;
+ }
+ } while (0);
+ $189 = $p_0;
+ if ($189 >>> 0 >= ($mem + (($10 & -8) - 8) | 0) >>> 0) {
+ _abort();
+ }
+ $194 = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] | 0;
+ if (($194 & 1 | 0) == 0) {
+ _abort();
+ }
+ HEAP32[sp + 656 >> 2] = 0;
+ HEAP32[sp + 660 >> 2] = 0;
+ HEAP32[sp + 240 >> 2] = $194;
+ HEAP32[sp + 32 >> 2] = $16;
+ HEAP32[sp + 216 >> 2] = $psize_0;
+ HEAP32[sp + 224 >> 2] = $p_0;
+ HEAP32[sp + 232 >> 2] = $189;
+ HEAP32[sp + 8 >> 2] = $mem;
+ HEAP32[sp + 24 >> 2] = $10;
+ sp = _free$2(sp) | 0;
+ tempValue = HEAP32[sp + 656 >> 2] | 0;
+ tempInt = HEAP32[sp + 660 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 660 >> 2];
+ HEAP32[sp + 656 >> 2] = 0;
+ HEAP32[sp + 660 >> 2] = 0;
+ if ((tempValue | 0) == 5) {
+ return;
+ }
+}
+function linear$0(sp) {
+ sp = sp | 0;
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ return sp | 0;
+}
+function linear$1(sp) {
+ sp = sp | 0;
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ cheez(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
+ return sp | 0;
+}
+function _free$0(sp) {
+ sp = sp | 0;
+ var $16 = 0, $220 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $227 = 0, $194 = 0, $233 = 0, $mem = 0, $10 = 0, $236 = 0, $_pre_phi305 = 0, $267 = 0, $270 = 0, $273 = 0, $294 = 0, $299 = 0, $R7_1 = 0, $R7_0 = 0, $RP9_0 = 0, $301 = 0, $302 = 0, $305 = 0, $306 = 0, $278 = 0, $320 = 0, $351 = 0, $364 = 0, $psize_1 = 0;
+ $psize_1 = HEAP32[sp + 424 >> 2] | 0;
+ $10 = HEAP32[sp + 24 >> 2] | 0;
+ $mem = HEAP32[sp + 8 >> 2] | 0;
+ $194 = HEAP32[sp + 240 >> 2] | 0;
+ $189 = HEAP32[sp + 232 >> 2] | 0;
+ $p_0 = HEAP32[sp + 224 >> 2] | 0;
+ $psize_0 = HEAP32[sp + 216 >> 2] | 0;
+ $16 = HEAP32[sp + 32 >> 2] | 0;
+ OL : do {
+ if (($16 | 0) == (HEAP32[25] | 0)) {
+ $220 = (HEAP32[22] | 0) + $psize_0 | 0;
+ HEAP32[22] = $220;
+ HEAP32[25] = $p_0;
+ HEAP32[$p_0 + 4 >> 2] = $220 | 1;
+ HEAP32[$189 + $220 >> 2] = $220;
+ HEAP32[sp + 640 >> 2] = 5;
+ break OL;
+ }
+ $227 = ($194 & -8) + $psize_0 | 0;
+ L726 : do {
+ if ($194 >>> 0 < 256) {
+ $233 = HEAP32[$mem + ($10 & -8) >> 2] | 0;
+ $236 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0;
+ do {
+ if (($233 | 0) != (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) {
+ if ($233 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[$233 + 12 >> 2] | 0) == ($16 | 0)) {
+ break;
+ }
+ _abort();
+ }
+ } while (0);
+ if (($236 | 0) == ($233 | 0)) {
+ HEAP32[20] = HEAP32[20] & (1 << ($194 >>> 3) ^ -1);
+ break;
+ }
+ do {
+ if (($236 | 0) == (120 + ($194 >>> 3 << 1 << 2) | 0 | 0)) {
+ $_pre_phi305 = $236 + 8 | 0;
+ } else {
+ if ($236 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($236 + 8 | 0) >> 2] | 0) == ($16 | 0)) {
+ $_pre_phi305 = $236 + 8 | 0;
+ break;
+ }
+ _abort();
+ }
+ } while (0);
+ HEAP32[$233 + 12 >> 2] = $236;
+ HEAP32[$_pre_phi305 >> 2] = $233;
+ } else {
+ $267 = $mem + (($10 & -8) - 8) | 0;
+ $270 = HEAP32[$mem + (($10 & -8) + 16) >> 2] | 0;
+ $273 = HEAP32[$mem + ($10 & -8 | 4) >> 2] | 0;
+ do {
+ if (($273 | 0) == ($267 | 0)) {
+ $294 = HEAP32[($mem + (($10 & -8) + 12) | 0) >> 2] | 0;
+ if (($294 | 0) == 0) {
+ $299 = HEAP32[($mem + (($10 & -8) + 8) | 0) >> 2] | 0;
+ if (($299 | 0) == 0) {
+ $R7_1 = 0;
+ break;
+ } else {
+ $R7_0 = $299;
+ $RP9_0 = $mem + (($10 & -8) + 8) | 0;
+ }
+ } else {
+ $R7_0 = $294;
+ $RP9_0 = $mem + (($10 & -8) + 12) | 0;
+ }
+ while (1) {
+ $301 = $R7_0 + 20 | 0;
+ $302 = HEAP32[$301 >> 2] | 0;
+ if (($302 | 0) != 0) {
+ $R7_0 = $302;
+ $RP9_0 = $301;
+ continue;
+ }
+ $305 = $R7_0 + 16 | 0;
+ $306 = HEAP32[$305 >> 2] | 0;
+ if (($306 | 0) == 0) {
+ break;
+ } else {
+ $R7_0 = $306;
+ $RP9_0 = $305;
+ }
+ }
+ if ($RP9_0 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$RP9_0 >> 2] = 0;
+ $R7_1 = $R7_0;
+ break;
+ }
+ } else {
+ $278 = HEAP32[$mem + ($10 & -8) >> 2] | 0;
+ if ($278 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($278 + 12 | 0) >> 2] | 0) != ($267 | 0)) {
+ _abort();
+ }
+ if ((HEAP32[($273 + 8 | 0) >> 2] | 0) == ($267 | 0)) {
+ HEAP32[($278 + 12 | 0) >> 2] = $273;
+ HEAP32[($273 + 8 | 0) >> 2] = $278;
+ $R7_1 = $273;
+ break;
+ } else {
+ _abort();
+ }
+ }
+ } while (0);
+ if (($270 | 0) == 0) {
+ break;
+ }
+ $320 = 384 + (HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] << 2) | 0;
+ do {
+ if (($267 | 0) == (HEAP32[$320 >> 2] | 0)) {
+ HEAP32[$320 >> 2] = $R7_1;
+ if (($R7_1 | 0) != 0) {
+ break;
+ }
+ HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + (($10 & -8) + 20) | 0) >> 2] ^ -1);
+ break L726;
+ } else {
+ if ($270 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($270 + 16 | 0) >> 2] | 0) == ($267 | 0)) {
+ HEAP32[($270 + 16 | 0) >> 2] = $R7_1;
+ } else {
+ HEAP32[$270 + 20 >> 2] = $R7_1;
+ }
+ if (($R7_1 | 0) == 0) {
+ break L726;
+ }
+ }
+ } while (0);
+ if ($R7_1 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ HEAP32[$R7_1 + 24 >> 2] = $270;
+ $351 = HEAP32[$mem + (($10 & -8) + 8) >> 2] | 0;
+ do {
+ if (($351 | 0) != 0) {
+ if ($351 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$R7_1 + 16 >> 2] = $351;
+ HEAP32[$351 + 24 >> 2] = $R7_1;
+ break;
+ }
+ }
+ } while (0);
+ $364 = HEAP32[$mem + (($10 & -8) + 12) >> 2] | 0;
+ if (($364 | 0) == 0) {
+ break;
+ }
+ if ($364 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$R7_1 + 20 >> 2] = $364;
+ HEAP32[$364 + 24 >> 2] = $R7_1;
+ break;
+ }
+ }
+ } while (0);
+ HEAP32[$p_0 + 4 >> 2] = $227 | 1;
+ HEAP32[$189 + $227 >> 2] = $227;
+ if (($p_0 | 0) != (HEAP32[25] | 0)) {
+ $psize_1 = $227;
+ HEAP32[sp + 640 >> 2] = 1;
+ break OL;
+ }
+ HEAP32[22] = $227;
+ HEAP32[sp + 640 >> 2] = 5;
+ break OL;
+ } while (0);
+ HEAP32[sp + 424 >> 2] = $psize_1;
+ return sp | 0;
+}
+function _free$1(sp) {
+ sp = sp | 0;
+ var $25 = 0, $mem = 0, $10 = 0, $26 = 0, $21 = 0, $37 = 0, $40 = 0, $5 = 0, $_pre_phi307 = 0, $69 = 0, $72 = 0, $75 = 0, $95 = 0, $100 = 0, $R_1 = 0, $R_0 = 0, $RP_0 = 0, $102 = 0, $103 = 0, $106 = 0, $107 = 0, $80 = 0, $120 = 0, $151 = 0, $164 = 0, $p_0 = 0, $psize_0 = 0;
+ $psize_0 = HEAP32[sp + 216 >> 2] | 0;
+ $p_0 = HEAP32[sp + 224 >> 2] | 0;
+ $5 = HEAP32[sp + 16 >> 2] | 0;
+ $21 = HEAP32[sp + 40 >> 2] | 0;
+ $26 = HEAP32[sp + 56 >> 2] | 0;
+ $10 = HEAP32[sp + 24 >> 2] | 0;
+ $mem = HEAP32[sp + 8 >> 2] | 0;
+ $25 = HEAP32[sp + 48 >> 2] | 0;
+ OL : do {
+ if (($25 | 0) == (HEAP32[25] | 0)) {
+ if ((HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & 3 | 0) != 3) {
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ HEAP32[22] = $26;
+ HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] & -2;
+ HEAP32[$mem + ((-8 - $21 | 0) + 4) >> 2] = $26 | 1;
+ HEAP32[($mem + (($10 & -8) - 8) | 0) >> 2] = $26;
+ HEAP32[sp + 648 >> 2] = 5;
+ break OL;
+ }
+ if ($21 >>> 0 < 256) {
+ $37 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0;
+ $40 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0;
+ do {
+ if (($37 | 0) != (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) {
+ if ($37 >>> 0 < $5 >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[$37 + 12 >> 2] | 0) == ($25 | 0)) {
+ break;
+ }
+ _abort();
+ }
+ } while (0);
+ if (($40 | 0) == ($37 | 0)) {
+ HEAP32[20] = HEAP32[20] & (1 << ($21 >>> 3) ^ -1);
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ do {
+ if (($40 | 0) == (120 + ($21 >>> 3 << 1 << 2) | 0 | 0)) {
+ $_pre_phi307 = $40 + 8 | 0;
+ } else {
+ if ($40 >>> 0 < $5 >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($40 + 8 | 0) >> 2] | 0) == ($25 | 0)) {
+ $_pre_phi307 = $40 + 8 | 0;
+ break;
+ }
+ _abort();
+ }
+ } while (0);
+ HEAP32[$37 + 12 >> 2] = $40;
+ HEAP32[$_pre_phi307 >> 2] = $37;
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ $69 = $mem + (-8 - $21 | 0) | 0;
+ $72 = HEAP32[$mem + ((-8 - $21 | 0) + 24) >> 2] | 0;
+ $75 = HEAP32[$mem + ((-8 - $21 | 0) + 12) >> 2] | 0;
+ do {
+ if (($75 | 0) == ($69 | 0)) {
+ $95 = HEAP32[($mem + ((-8 - $21 | 0) + 20) | 0) >> 2] | 0;
+ if (($95 | 0) == 0) {
+ $100 = HEAP32[($mem + ((-8 - $21 | 0) + 16) | 0) >> 2] | 0;
+ if (($100 | 0) == 0) {
+ $R_1 = 0;
+ break;
+ } else {
+ $R_0 = $100;
+ $RP_0 = $mem + ((-8 - $21 | 0) + 16) | 0;
+ }
+ } else {
+ $R_0 = $95;
+ $RP_0 = $mem + ((-8 - $21 | 0) + 20) | 0;
+ }
+ while (1) {
+ $102 = $R_0 + 20 | 0;
+ $103 = HEAP32[$102 >> 2] | 0;
+ if (($103 | 0) != 0) {
+ $R_0 = $103;
+ $RP_0 = $102;
+ continue;
+ }
+ $106 = $R_0 + 16 | 0;
+ $107 = HEAP32[$106 >> 2] | 0;
+ if (($107 | 0) == 0) {
+ break;
+ } else {
+ $R_0 = $107;
+ $RP_0 = $106;
+ }
+ }
+ if ($RP_0 >>> 0 < $5 >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$RP_0 >> 2] = 0;
+ $R_1 = $R_0;
+ break;
+ }
+ } else {
+ $80 = HEAP32[$mem + ((-8 - $21 | 0) + 8) >> 2] | 0;
+ if ($80 >>> 0 < $5 >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($80 + 12 | 0) >> 2] | 0) != ($69 | 0)) {
+ _abort();
+ }
+ if ((HEAP32[($75 + 8 | 0) >> 2] | 0) == ($69 | 0)) {
+ HEAP32[($80 + 12 | 0) >> 2] = $75;
+ HEAP32[($75 + 8 | 0) >> 2] = $80;
+ $R_1 = $75;
+ break;
+ } else {
+ _abort();
+ }
+ }
+ } while (0);
+ if (($72 | 0) == 0) {
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ $120 = 384 + (HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] << 2) | 0;
+ do {
+ if (($69 | 0) == (HEAP32[$120 >> 2] | 0)) {
+ HEAP32[$120 >> 2] = $R_1;
+ if (($R_1 | 0) != 0) {
+ break;
+ }
+ HEAP32[21] = HEAP32[21] & (1 << HEAP32[($mem + ((-8 - $21 | 0) + 28) | 0) >> 2] ^ -1);
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 2;
+ HEAP32[sp + 652 >> 2] = 2;
+ break OL;
+ } else {
+ if ($72 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ if ((HEAP32[($72 + 16 | 0) >> 2] | 0) == ($69 | 0)) {
+ HEAP32[($72 + 16 | 0) >> 2] = $R_1;
+ } else {
+ HEAP32[$72 + 20 >> 2] = $R_1;
+ }
+ if (($R_1 | 0) == 0) {
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 2;
+ HEAP32[sp + 652 >> 2] = 2;
+ break OL;
+ }
+ }
+ } while (0);
+ if ($R_1 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ }
+ HEAP32[$R_1 + 24 >> 2] = $72;
+ $151 = HEAP32[$mem + ((-8 - $21 | 0) + 16) >> 2] | 0;
+ do {
+ if (($151 | 0) != 0) {
+ if ($151 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$R_1 + 16 >> 2] = $151;
+ HEAP32[$151 + 24 >> 2] = $R_1;
+ break;
+ }
+ }
+ } while (0);
+ $164 = HEAP32[$mem + ((-8 - $21 | 0) + 20) >> 2] | 0;
+ if (($164 | 0) == 0) {
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ if ($164 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$R_1 + 20 >> 2] = $164;
+ HEAP32[$164 + 24 >> 2] = $R_1;
+ $p_0 = $25;
+ $psize_0 = $26;
+ HEAP32[sp + 648 >> 2] = 1;
+ break OL;
+ }
+ } while (0);
+ HEAP32[sp + 224 >> 2] = $p_0;
+ HEAP32[sp + 216 >> 2] = $psize_0;
+ return sp | 0;
+}
+function _free$2(sp) {
+ sp = sp | 0;
+ var $194 = 0, $16 = 0, $204 = 0, $psize_0 = 0, $p_0 = 0, $189 = 0, $mem = 0, $10 = 0, $psize_1 = 0, $390 = 0, $396 = 0, $F16_0 = 0, $_pre_phi = 0, $404 = 0, $414 = 0, $415 = 0, $I18_0 = 0, $428 = 0, $436 = 0, $443 = 0, $447 = 0, $448 = 0, $463 = 0, $K19_0 = 0, $T_0 = 0, $472 = 0, $473 = 0, label = 0, $486 = 0, $487 = 0, $489 = 0, $501 = 0, $sp_0_in_i = 0, $sp_0_i = 0;
+ $10 = HEAP32[sp + 24 >> 2] | 0;
+ $mem = HEAP32[sp + 8 >> 2] | 0;
+ $189 = HEAP32[sp + 232 >> 2] | 0;
+ $p_0 = HEAP32[sp + 224 >> 2] | 0;
+ $psize_0 = HEAP32[sp + 216 >> 2] | 0;
+ $16 = HEAP32[sp + 32 >> 2] | 0;
+ $194 = HEAP32[sp + 240 >> 2] | 0;
+ OL : do {
+ do {
+ if (($194 & 2 | 0) == 0) {
+ if (($16 | 0) == (HEAP32[26] | 0)) {
+ $204 = (HEAP32[23] | 0) + $psize_0 | 0;
+ HEAP32[23] = $204;
+ HEAP32[26] = $p_0;
+ HEAP32[$p_0 + 4 >> 2] = $204 | 1;
+ if (($p_0 | 0) == (HEAP32[25] | 0)) {
+ HEAP32[25] = 0;
+ HEAP32[22] = 0;
+ }
+ if ($204 >>> 0 <= (HEAP32[27] | 0) >>> 0) {
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ }
+ _sys_trim(0) | 0;
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ }
+ HEAP32[sp + 640 >> 2] = 0;
+ HEAP32[sp + 644 >> 2] = 0;
+ HEAP32[sp + 32 >> 2] = $16;
+ HEAP32[sp + 216 >> 2] = $psize_0;
+ HEAP32[sp + 224 >> 2] = $p_0;
+ HEAP32[sp + 232 >> 2] = $189;
+ HEAP32[sp + 240 >> 2] = $194;
+ HEAP32[sp + 8 >> 2] = $mem;
+ HEAP32[sp + 24 >> 2] = $10;
+ HEAP32[sp + 424 >> 2] = $psize_1;
+ sp = _free$0(sp) | 0;
+ $psize_1 = HEAP32[sp + 424 >> 2] | 0;
+ tempValue = HEAP32[sp + 640 >> 2] | 0;
+ tempInt = HEAP32[sp + 644 >> 2] | 0;
+ tempDouble = +HEAPF32[sp + 644 >> 2];
+ HEAP32[sp + 640 >> 2] = 0;
+ HEAP32[sp + 644 >> 2] = 0;
+ if ((tempValue | 0) == 5) {
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ }
+ if ((tempValue | 0) == 1) {
+ break;
+ }
+ } else {
+ HEAP32[($mem + (($10 & -8) - 4) | 0) >> 2] = $194 & -2;
+ HEAP32[$p_0 + 4 >> 2] = $psize_0 | 1;
+ HEAP32[$189 + $psize_0 >> 2] = $psize_0;
+ $psize_1 = $psize_0;
+ }
+ } while (0);
+ $390 = $psize_1 >>> 3;
+ if ($psize_1 >>> 0 < 256) {
+ $396 = HEAP32[20] | 0;
+ do {
+ if (($396 & 1 << $390 | 0) == 0) {
+ HEAP32[20] = $396 | 1 << $390;
+ $F16_0 = 120 + ($390 << 1 << 2) | 0;
+ $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0;
+ } else {
+ $404 = HEAP32[(120 + (($390 << 1) + 2 << 2) | 0) >> 2] | 0;
+ if ($404 >>> 0 >= (HEAP32[24] | 0) >>> 0) {
+ $F16_0 = $404;
+ $_pre_phi = 120 + (($390 << 1) + 2 << 2) | 0;
+ break;
+ }
+ _abort();
+ }
+ } while (0);
+ HEAP32[$_pre_phi >> 2] = $p_0;
+ HEAP32[$F16_0 + 12 >> 2] = $p_0;
+ HEAP32[$p_0 + 8 >> 2] = $F16_0;
+ HEAP32[$p_0 + 12 >> 2] = 120 + ($390 << 1 << 2) | 0;
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ }
+ $414 = $p_0;
+ $415 = $psize_1 >>> 8;
+ do {
+ if (($415 | 0) == 0) {
+ $I18_0 = 0;
+ } else {
+ if ($psize_1 >>> 0 > 16777215) {
+ $I18_0 = 31;
+ break;
+ }
+ $428 = $415 << (($415 + 1048320 | 0) >>> 16 & 8) << ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4);
+ $436 = 14 - ((($415 << (($415 + 1048320 | 0) >>> 16 & 8)) + 520192 | 0) >>> 16 & 4 | ($415 + 1048320 | 0) >>> 16 & 8 | ($428 + 245760 | 0) >>> 16 & 2) + ($428 << (($428 + 245760 | 0) >>> 16 & 2) >>> 15) | 0;
+ $I18_0 = $psize_1 >>> (($436 + 7 | 0) >>> 0) & 1 | $436 << 1;
+ }
+ } while (0);
+ $443 = 384 + ($I18_0 << 2) | 0;
+ HEAP32[$p_0 + 28 >> 2] = $I18_0;
+ HEAP32[$p_0 + 20 >> 2] = 0;
+ HEAP32[$p_0 + 16 >> 2] = 0;
+ $447 = HEAP32[21] | 0;
+ $448 = 1 << $I18_0;
+ do {
+ if (($447 & $448 | 0) == 0) {
+ HEAP32[21] = $447 | $448;
+ HEAP32[$443 >> 2] = $414;
+ HEAP32[$p_0 + 24 >> 2] = $443;
+ HEAP32[$p_0 + 12 >> 2] = $p_0;
+ HEAP32[$p_0 + 8 >> 2] = $p_0;
+ } else {
+ if (($I18_0 | 0) == 31) {
+ $463 = 0;
+ } else {
+ $463 = 25 - ($I18_0 >>> 1) | 0;
+ }
+ $K19_0 = $psize_1 << $463;
+ $T_0 = HEAP32[$443 >> 2] | 0;
+ while (1) {
+ if ((HEAP32[$T_0 + 4 >> 2] & -8 | 0) == ($psize_1 | 0)) {
+ break;
+ }
+ $472 = $T_0 + 16 + ($K19_0 >>> 31 << 2) | 0;
+ $473 = HEAP32[$472 >> 2] | 0;
+ if (($473 | 0) == 0) {
+ label = 569;
+ break;
+ } else {
+ $K19_0 = $K19_0 << 1;
+ $T_0 = $473;
+ }
+ }
+ if ((label | 0) == 569) {
+ if ($472 >>> 0 < (HEAP32[24] | 0) >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$472 >> 2] = $414;
+ HEAP32[$p_0 + 24 >> 2] = $T_0;
+ HEAP32[$p_0 + 12 >> 2] = $p_0;
+ HEAP32[$p_0 + 8 >> 2] = $p_0;
+ break;
+ }
+ }
+ $486 = $T_0 + 8 | 0;
+ $487 = HEAP32[$486 >> 2] | 0;
+ $489 = HEAP32[24] | 0;
+ if ($T_0 >>> 0 < $489 >>> 0) {
+ _abort();
+ }
+ if ($487 >>> 0 < $489 >>> 0) {
+ _abort();
+ } else {
+ HEAP32[$487 + 12 >> 2] = $414;
+ HEAP32[$486 >> 2] = $414;
+ HEAP32[$p_0 + 8 >> 2] = $487;
+ HEAP32[$p_0 + 12 >> 2] = $T_0;
+ HEAP32[$p_0 + 24 >> 2] = 0;
+ break;
+ }
+ }
+ } while (0);
+ $501 = (HEAP32[28] | 0) - 1 | 0;
+ HEAP32[28] = $501;
+ if (($501 | 0) == 0) {
+ $sp_0_in_i = 536;
+ } else {
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ }
+ while (1) {
+ $sp_0_i = HEAP32[$sp_0_in_i >> 2] | 0;
+ if (($sp_0_i | 0) == 0) {
+ break;
+ } else {
+ $sp_0_in_i = $sp_0_i + 8 | 0;
+ }
+ }
+ HEAP32[28] = -1;
+ STACKTOP = sp;
+ HEAP32[sp + 656 >> 2] = 5;
+ break OL;
+ } while (0);
+ return sp | 0;
+}
+
diff --git a/tools/test-js-optimizer-asm-outline.js b/tools/test-js-optimizer-asm-outline2.js
index 153d7feb..5da6e04a 100644
--- a/tools/test-js-optimizer-asm-outline.js
+++ b/tools/test-js-optimizer-asm-outline2.js
@@ -603,4 +603,4 @@ function _free($mem) {
return;
}
// EMSCRIPTEN_GENERATED_FUNCTIONS
-// EXTRA_INFO: { "sizeToOutline": 1000 }
+// EXTRA_INFO: { "sizeToOutline": 1000, "allowCostlyOutlines": 1 }
diff --git a/tools/test-js-optimizer-asm-pre-output.js b/tools/test-js-optimizer-asm-pre-output.js
index 8a5803d6..b31327f2 100644
--- a/tools/test-js-optimizer-asm-pre-output.js
+++ b/tools/test-js-optimizer-asm-pre-output.js
@@ -254,7 +254,7 @@ function _main($argc, $argv) {
$j_08_i_i = 0;
$i_09_i_i = 1;
while (1) {
- if (!(($j_08_i_i | 0) < 14)) {
+ if (($j_08_i_i | 0) >= 14) {
label = 49;
break;
}
@@ -361,7 +361,7 @@ function _main($argc, $argv) {
$118 = $world + 102952 | 0;
HEAP32[$116 + 96 >> 2] = HEAP32[$118 >> 2];
$121 = HEAP32[$118 >> 2] | 0;
- if (!(($121 | 0) == 0)) {
+ if (($121 | 0) != 0) {
HEAP32[$121 + 92 >> 2] = $116;
}
HEAP32[$118 >> 2] = $116;
@@ -451,7 +451,7 @@ function _main($argc, $argv) {
$y_sroa_0_0_insert_insert$1 = +$y_sroa_1_4_load293550;
HEAPF32[$185 >> 2] = $y_sroa_0_0_load283451;
HEAPF32[$185 + 4 >> 2] = $y_sroa_0_0_insert_insert$1;
- if (!((HEAP32[$98 >> 2] & 2 | 0) == 0)) {
+ if ((HEAP32[$98 >> 2] & 2 | 0) != 0) {
label = 65;
break L82;
}
@@ -466,7 +466,7 @@ function _main($argc, $argv) {
HEAP32[$209 + 92 >> 2] = 0;
HEAP32[$209 + 96 >> 2] = HEAP32[$118 >> 2];
$213 = HEAP32[$118 >> 2] | 0;
- if (!(($213 | 0) == 0)) {
+ if (($213 | 0) != 0) {
HEAP32[$213 + 92 >> 2] = $209;
}
HEAP32[$118 >> 2] = $209;