aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js107
-rw-r--r--src/compiler.js8
-rw-r--r--src/corruptionCheck.js6
-rw-r--r--[-rwxr-xr-x]src/embind/embind.js125
-rw-r--r--[-rwxr-xr-x]src/embind/emval.js177
-rw-r--r--src/intertyper.js12
-rw-r--r--src/jsifier.js103
-rw-r--r--src/library.js316
-rw-r--r--src/library_browser.js53
-rw-r--r--src/library_egl.js4
-rw-r--r--src/library_gc.js7
-rw-r--r--src/library_gl.js317
-rw-r--r--src/library_glfw.js29
-rw-r--r--src/library_glut.js41
-rw-r--r--src/library_sdl.js174
-rw-r--r--src/modules.js48
-rw-r--r--src/parseTools.js61
-rw-r--r--src/postamble.js10
-rw-r--r--src/preamble.js69
-rwxr-xr-xsrc/relooper/doit.sh2
-rwxr-xr-xsrc/relooper/testit.sh2
-rwxr-xr-xsrc/relooper/updateit.sh1
-rw-r--r--src/runtime.js49
-rw-r--r--src/settings.js19
24 files changed, 1273 insertions, 467 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 7fbdf24d..2cc46ab6 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -20,6 +20,7 @@ var BRANCH_INVOKE = set('branch', 'invoke');
var LABEL_ENDERS = set('branch', 'return', 'switch');
var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic');
var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam');
+var I64_DOUBLE_FLIP = { i64: 'double', double: 'i64' };
// Analyzer
@@ -99,7 +100,86 @@ function analyzer(data, sidePass) {
}
}
delete item.items;
+ this.forwardItem(item, 'CastAway');
+ }
+ });
+
+ // CastAway - try to remove bitcasts of double<-->i64, which LLVM sometimes generates unnecessarily
+ // (load a double, convert to i64, use as i64).
+ // We optimize this by checking if there are such bitcasts. If so we create a shadow
+ // variable that is of the other type, and use that in the relevant places. (As SSA, this is valid, and
+ // variable elimination later will remove the double load if it is no longer needed.)
+ //
+ // Note that aside from being an optimization, this is needed for correctness in some cases: If code
+ // assumes it can bitcast a double to an i64 and back and forth without loss, that may be violated
+ // due to NaN canonicalization.
+ substrate.addActor('CastAway', {
+ processItem: function(item) {
this.forwardItem(item, 'Legalizer');
+ if (USE_TYPED_ARRAYS != 2) return;
+
+ item.functions.forEach(function(func) {
+ var has = false;
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP) {
+ has = true;
+ }
+ }
+ });
+ if (!has) return;
+ // there are i64<-->double bitcasts, create shadows for everything
+ var shadowed = {};
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ var i = 0;
+ while (i < lines.length) {
+ var lines = label.lines;
+ var line = lines[i];
+ if (line.intertype == 'load' && line.type in I64_DOUBLE_FLIP) {
+ if (line.pointer.intertype != 'value') { i++; continue } // TODO
+ shadowed[line.assignTo] = 1;
+ var shadow = line.assignTo + '$$SHADOW';
+ var flip = I64_DOUBLE_FLIP[line.type];
+ lines.splice(i + 1, 0, { // if necessary this element will be legalized in the next phase
+ tokens: null,
+ indent: 2,
+ lineNum: line.lineNum + 0.5,
+ assignTo: shadow,
+ intertype: 'load',
+ pointerType: flip + '*',
+ type: flip,
+ valueType: flip,
+ pointer: {
+ intertype: 'value',
+ ident: line.pointer.ident,
+ type: flip + '*'
+ },
+ align: line.align,
+ ident: line.ident
+ });
+ // note: no need to update func.lines, it is generated in a later pass
+ i++;
+ }
+ i++;
+ }
+ });
+ // use shadows where possible
+ func.labels.forEach(function(label) {
+ var lines = label.lines;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
+ if (line.intertype == 'bitcast' && line.type in I64_DOUBLE_FLIP && line.ident in shadowed) {
+ var shadow = line.ident + '$$SHADOW';
+ line.params[0].ident = shadow;
+ line.params[0].type = line.type;
+ line.type2 = line.type;
+ }
+ }
+ });
+ });
}
});
@@ -174,12 +254,15 @@ function analyzer(data, sidePass) {
var factor = (next - prev)/(4*toAdd.length+3);
for (var k = 0; k < toAdd.length; k++) {
toAdd[k].lineNum = prev + ((k+1)*factor);
+ assert(k == 0 || toAdd[k].lineNum > toAdd[k-1].lineNum);
}
}
function removeAndAdd(lines, i, toAdd) {
var item = lines[i];
interpLines(lines, i, toAdd);
Array.prototype.splice.apply(lines, [i, 1].concat(toAdd));
+ if (i > 0) assert(lines[i].lineNum > lines[i-1].lineNum);
+ if (i + toAdd.length < lines.length) assert(lines[i + toAdd.length - 1].lineNum < lines[i + toAdd.length].lineNum);
return toAdd.length;
}
function legalizeFunctionParameters(params) {
@@ -328,8 +411,9 @@ function analyzer(data, sidePass) {
// legalize parameters
legalizeFunctionParameters(value.params);
// legalize return value, if any
- if (value.assignTo && isIllegalType(item.type)) {
- bits = getBits(value.type);
+ var returnType = getReturnType(item.type);
+ if (value.assignTo && isIllegalType(returnType)) {
+ bits = getBits(returnType);
var elements = getLegalVars(item.assignTo, bits);
// legalize return value
value.assignTo = elements[0].ident;
@@ -469,6 +553,23 @@ function analyzer(data, sidePass) {
i++;
continue; // special case, handled in makeComparison
}
+ case 'va_arg': {
+ assert(value.type == 'i64');
+ assert(value.value.type == 'i32*', value.value.type);
+ i += removeAndAdd(label.lines, i, range(2).map(function(x) {
+ return {
+ intertype: 'va_arg',
+ assignTo: value.assignTo + '$' + x,
+ type: 'i32',
+ value: {
+ intertype: 'value',
+ ident: value.value.ident, // We read twice from the same i32* var, incrementing // + '$' + x,
+ type: 'i32*'
+ }
+ };
+ }));
+ continue;
+ }
case 'extractvalue': { // XXX we assume 32-bit alignment in extractvalue/insertvalue,
// but in theory they can run on packed structs too (see use getStructuralTypePartBits)
// potentially legalize the actual extracted value too if it is >32 bits, not just the extraction in general
@@ -1492,7 +1593,7 @@ function analyzer(data, sidePass) {
calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0;
if (USE_TYPED_ARRAYS === 2) {
// We need to keep the stack aligned
- item.allocatedSize = Runtime.forceAlign(item.allocatedSize, QUANTUM_SIZE);
+ item.allocatedSize = Runtime.forceAlign(item.allocatedSize, Runtime.STACK_ALIGN);
}
}
var index = 0;
diff --git a/src/compiler.js b/src/compiler.js
index 313fd5f7..94e77e26 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -183,7 +183,8 @@ if (ASM_JS) {
assert(!ALLOW_MEMORY_GROWTH, 'Cannot grow asm.js heap');
assert((TOTAL_MEMORY&(TOTAL_MEMORY-1)) == 0, 'asm.js heap must be power of 2');
}
-assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB)); // shared libraries must have named globals
+assert(!BUILD_AS_SHARED_LIB, 'shared libs are deprecated');
+//assert(!(!NAMED_GLOBALS && BUILD_AS_SHARED_LIB), 'shared libraries must have named globals');
// Output some info and warnings based on settings
@@ -208,7 +209,10 @@ load('parseTools.js');
load('intertyper.js');
load('analyzer.js');
load('jsifier.js');
-if (RELOOP) load(RELOOPER)
+if (RELOOP) {
+ load(RELOOPER);
+ assert(typeof Relooper != 'undefined');
+}
globalEval(processMacros(preprocess(read('runtime.js'))));
Runtime.QUANTUM_SIZE = QUANTUM_SIZE;
diff --git a/src/corruptionCheck.js b/src/corruptionCheck.js
index 315f5cf0..8b37120a 100644
--- a/src/corruptionCheck.js
+++ b/src/corruptionCheck.js
@@ -42,7 +42,7 @@ var CorruptionChecker = {
CorruptionChecker.checkAll();
var size = CorruptionChecker.ptrs[ptr];
//Module.printErr('free ' + ptr + ' of size ' + size);
- assert(size);
+ assert(size, ptr);
var allocation = ptr - size*CorruptionChecker.BUFFER_FACTOR;
//Module.printErr('free ' + ptr + ' of size ' + size + ' and allocation ' + allocation);
delete CorruptionChecker.ptrs[ptr];
@@ -67,12 +67,12 @@ var CorruptionChecker = {
},
fillBuffer: function(buffer, size) {
for (var x = buffer; x < buffer + size; x++) {
- {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8') }}};
+ {{{ makeSetValue('x', 0, 'CorruptionChecker.canary(x)', 'i8', null, null, null, 1) }}};
}
},
checkBuffer: function(buffer, size) {
for (var x = buffer; x < buffer + size; x++) {
- if (({{{ makeGetValue('x', 0, 'i8') }}}&255) != CorruptionChecker.canary(x)) {
+ if (({{{ makeGetValue('x', 0, 'i8', null, null, null, null, 1) }}}&255) != CorruptionChecker.canary(x)) {
assert(0, 'Heap corruption detected!' + [x, buffer, size, {{{ makeGetValue('x', 0, 'i8') }}}&255, CorruptionChecker.canary(x)]);
}
}
diff --git a/src/embind/embind.js b/src/embind/embind.js
index 988526b4..91386c69 100755..100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -131,6 +131,7 @@ function extendError(baseErrorType, errorName) {
// from https://github.com/imvu/imvujs/blob/master/src/function.js
function createNamedFunction(name, body) {
+ name = makeLegalFunctionName(name);
/*jshint evil:true*/
return new Function(
"body",
@@ -270,6 +271,10 @@ function __embind_register_void(rawType, name) {
'fromWireType': function() {
return undefined;
},
+ 'toWireType': function(destructors, o) {
+ // TODO: assert if anything else is given?
+ return undefined;
+ },
});
}
@@ -306,7 +311,7 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following two if()s and assume value is of proper type.
- if (typeof value !== "number") {
+ if (typeof value !== "number" && typeof value !== "boolean") {
throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name);
}
if (value < minRange || value > maxRange) {
@@ -328,8 +333,8 @@ function __embind_register_float(rawType, name) {
'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following if() and assume value is of proper type.
- if (typeof value !== "number") {
- throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' +this.name);
+ if (typeof value !== "number" && typeof value !== "boolean") {
+ throw new TypeError('Cannot convert "' + _embind_repr(value) + '" to ' + this.name);
}
return value;
},
@@ -449,6 +454,31 @@ function __embind_register_emval(rawType, name) {
});
}
+function __embind_register_memory_view(rawType, name) {
+ var typeMapping = [
+ Int8Array,
+ Uint8Array,
+ Int16Array,
+ Uint16Array,
+ Int32Array,
+ Uint32Array,
+ Float32Array,
+ Float64Array,
+ ];
+
+ name = readLatin1String(name);
+ registerType(rawType, {
+ name: name,
+ 'fromWireType': function(handle) {
+ var type = HEAPU32[handle >> 2];
+ var size = HEAPU32[(handle >> 2) + 1]; // in elements
+ var data = HEAPU32[(handle >> 2) + 2]; // byte offset into emscripten heap
+ var TA = typeMapping[type];
+ return new TA(HEAP8.buffer, data, size);
+ },
+ });
+}
+
function runDestructors(destructors) {
while (destructors.length) {
var ptr = destructors.pop();
@@ -677,7 +707,7 @@ function __embind_finalize_tuple(rawTupleType) {
},
'toWireType': function(destructors, o) {
if (elementsLength !== o.length) {
- throw new TypeError("Incorrect number of tuple elements");
+ throw new TypeError("Incorrect number of tuple elements for " + reg.name + ": expected=" + elementsLength + ", actual=" + o.length);
}
var ptr = rawConstructor();
for (var i = 0; i < elementsLength; ++i) {
@@ -685,7 +715,7 @@ function __embind_finalize_tuple(rawTupleType) {
}
if (destructors !== null) {
destructors.push(rawDestructor, ptr);
- }
+ }
return ptr;
},
destructorFunction: rawDestructor,
@@ -802,7 +832,9 @@ var genericPointerToWireType = function(destructors, handle) {
if (this.isSmartPointer) {
var ptr = this.rawConstructor();
- destructors.push(this.rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(this.rawDestructor, ptr);
+ }
return ptr;
} else {
return 0;
@@ -854,7 +886,9 @@ var genericPointerToWireType = function(destructors, handle) {
clonedHandle.delete();
})
);
- destructors.push(this.rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(this.rawDestructor, ptr);
+ }
}
break;
@@ -1054,9 +1088,39 @@ function getInstanceTypeName(handle) {
return handle.$$.ptrType.registeredClass.name;
}
+ClassHandle.prototype.isAliasOf = function(other) {
+ if (!(this instanceof ClassHandle)) {
+ return false;
+ }
+ if (!(other instanceof ClassHandle)) {
+ return false;
+ }
+
+ var leftClass = this.$$.ptrType.registeredClass;
+ var left = this.$$.ptr;
+ var rightClass = other.$$.ptrType.registeredClass;
+ var right = other.$$.ptr;
+
+ while (leftClass.baseClass) {
+ left = leftClass.upcast(left);
+ leftClass = leftClass.baseClass;
+ }
+
+ while (rightClass.baseClass) {
+ right = rightClass.upcast(right);
+ rightClass = rightClass.baseClass;
+ }
+
+ return leftClass === rightClass && left === right;
+};
+
+function throwInstanceAlreadyDeleted(obj) {
+ throwBindingError(getInstanceTypeName(obj) + ' instance already deleted');
+}
+
ClassHandle.prototype.clone = function() {
if (!this.$$.ptr) {
- throwBindingError(getInstanceTypeName(this) + ' instance already deleted');
+ throwInstanceAlreadyDeleted(this);
}
var clone = Object.create(Object.getPrototypeOf(this), {
@@ -1078,9 +1142,12 @@ function runDestructor(handle) {
}
}
-ClassHandle.prototype['delete'] = function() {
+ClassHandle.prototype['delete'] = function ClassHandle_delete() {
if (!this.$$.ptr) {
- throwBindingError(getInstanceTypeName(this) + ' instance already deleted');
+ throwInstanceAlreadyDeleted(this);
+ }
+ if (this.$$.deleteScheduled) {
+ throwBindingError('Object already scheduled for deletion');
}
this.$$.count.value -= 1;
@@ -1090,6 +1157,44 @@ ClassHandle.prototype['delete'] = function() {
this.$$.smartPtr = undefined;
this.$$.ptr = undefined;
};
+
+var deletionQueue = [];
+
+ClassHandle.prototype['isDeleted'] = function isDeleted() {
+ return !this.$$.ptr;
+};
+
+ClassHandle.prototype['deleteLater'] = function deleteLater() {
+ if (!this.$$.ptr) {
+ throwInstanceAlreadyDeleted(this);
+ }
+ if (this.$$.deleteScheduled) {
+ throwBindingError('Object already scheduled for deletion');
+ }
+ deletionQueue.push(this);
+ if (deletionQueue.length === 1 && delayFunction) {
+ delayFunction(flushPendingDeletes);
+ }
+ this.$$.deleteScheduled = true;
+ return this;
+};
+
+function flushPendingDeletes() {
+ while (deletionQueue.length) {
+ var obj = deletionQueue.pop();
+ obj.$$.deleteScheduled = false;
+ obj['delete']();
+ }
+}
+Module['flushPendingDeletes'] = flushPendingDeletes;
+
+var delayFunction;
+Module['setDelayFunction'] = function setDelayFunction(fn) {
+ delayFunction = fn;
+ if (deletionQueue.length && delayFunction) {
+ delayFunction(flushPendingDeletes);
+ }
+};
function RegisteredClass(
name,
diff --git a/src/embind/emval.js b/src/embind/emval.js
index c02ffa92..77270597 100755..100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -1,8 +1,12 @@
-/*global Module*/
+/*global Module:true, Runtime*/
/*global HEAP32*/
+/*global new_*/
+/*global createNamedFunction*/
/*global readLatin1String, writeStringToMemory*/
/*global requireRegisteredType, throwBindingError*/
+var Module = Module || {};
+
var _emval_handle_array = [{}]; // reserve zero
var _emval_free_list = [];
@@ -69,14 +73,8 @@ function __emval_incref(handle) {
function __emval_decref(handle) {
if (handle && 0 === --_emval_handle_array[handle].refcount) {
- delete _emval_handle_array[handle];
+ _emval_handle_array[handle] = undefined;
_emval_free_list.push(handle);
-
- var actual_length = _emval_handle_array.length;
- while (actual_length > 0 && _emval_handle_array[actual_length - 1] === undefined) {
- --actual_length;
- }
- _emval_handle_array.length = actual_length;
}
}
@@ -108,44 +106,73 @@ function __emval_take_value(type, v) {
var __newers = {}; // arity -> function
-function __emval_new(handle, argCount, argTypes) {
- requireHandle(handle);
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 3));
+function craftEmvalAllocator(argCount) {
+ /*This function returns a new function that looks like this:
+ function emval_allocator_3(handle, argTypes, arg0Wired, arg1Wired, arg2Wired) {
+ var argType0 = requireRegisteredType(HEAP32[(argTypes >> 2)], "parameter 0");
+ var arg0 = argType0.fromWireType(arg0Wired);
+ var argType1 = requireRegisteredType(HEAP32[(argTypes >> 2) + 1], "parameter 1");
+ var arg1 = argType1.fromWireType(arg1Wired);
+ var argType2 = requireRegisteredType(HEAP32[(argTypes >> 2) + 2], "parameter 2");
+ var arg2 = argType2.fromWireType(arg2Wired);
+ var constructor = _emval_handle_array[handle].value;
+ var emval = new constructor(arg0, arg1, arg2);
+ return emval;
+ } */
+
+ var args1 = ["requireRegisteredType", "HEAP32", "_emval_handle_array", "__emval_register"];
+ var args2 = [requireRegisteredType, HEAP32, _emval_handle_array, __emval_register];
+
+ var argsList = "";
+ var argsListWired = "";
+ for(var i = 0; i < argCount; ++i) {
+ argsList += (i!==0?", ":"")+"arg"+i; // 'arg0, arg1, ..., argn'
+ argsListWired += ", arg"+i+"Wired"; // ', arg0Wired, arg1Wired, ..., argnWired'
+ }
- // Alas, we are forced to use operator new until WebKit enables
- // constructing typed arrays without new.
- // In WebKit, Uint8Array(10) throws an error.
- // In every other browser, it's identical to new Uint8Array(10).
+ var invokerFnBody =
+ "return function emval_allocator_"+argCount+"(handle, argTypes " + argsListWired + ") {\n";
+ for(var i = 0; i < argCount; ++i) {
+ invokerFnBody +=
+ "var argType"+i+" = requireRegisteredType(HEAP32[(argTypes >> 2) + "+i+"], \"parameter "+i+"\");\n" +
+ "var arg"+i+" = argType"+i+".fromWireType(arg"+i+"Wired);\n";
+ }
+ invokerFnBody +=
+ "var constructor = _emval_handle_array[handle].value;\n" +
+ "var obj = new constructor("+argsList+");\n" +
+ "return __emval_register(obj);\n" +
+ "}\n";
+
+ args1.push(invokerFnBody);
+ var invokerFunction = new_(Function, args1).apply(null, args2);
+ return invokerFunction;
+}
+
+function __emval_new(handle, argCount, argTypes) {
+ requireHandle(handle);
+
var newer = __newers[argCount];
if (!newer) {
- var parameters = new Array(argCount);
- for (var i = 0; i < argCount; ++i) {
- parameters[i] = 'a' + i;
- }
- /*jshint evil:true*/
- newer = __newers[argCount] = new Function(
- ['c'].concat(parameters),
- "return new c(" + parameters.join(',') + ");");
+ newer = craftEmvalAllocator(argCount);
+ __newers[argCount] = newer;
}
-
- var constructor = _emval_handle_array[handle].value;
- var obj = newer.apply(undefined, [constructor].concat(args));
-/*
- // implement what amounts to operator new
- function dummy(){}
- dummy.prototype = constructor.prototype;
- var obj = new constructor;
- var rv = constructor.apply(obj, args);
- if (typeof rv === 'object') {
- obj = rv;
+
+ if (argCount === 0) {
+ return newer(handle, argTypes);
+ } else if (argCount === 1) {
+ return newer(handle, argTypes, arguments[3]);
+ } else if (argCount === 2) {
+ return newer(handle, argTypes, arguments[3], arguments[4]);
+ } else if (argCount === 3) {
+ return newer(handle, argTypes, arguments[3], arguments[4], arguments[5]);
+ } else if (argCount === 4) {
+ return newer(handle, argTypes, arguments[3], arguments[4], arguments[5], arguments[6]);
+ } else {
+ // This is a slow path! (.apply and .splice are slow), so a few specializations are present above.
+ return newer.apply(null, arguments.splice(1));
}
-*/
- return __emval_register(obj);
}
// appease jshint (technically this code uses eval)
@@ -192,38 +219,62 @@ function parseParameters(argCount, argTypes, argWireTypes) {
function __emval_call(handle, argCount, argTypes) {
requireHandle(handle);
+ var types = lookupTypes(argCount, argTypes);
+
+ var args = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ args[i] = types[i].fromWireType(arguments[3 + i]);
+ }
+
var fn = _emval_handle_array[handle].value;
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 3));
var rv = fn.apply(undefined, args);
return __emval_register(rv);
}
-function __emval_call_method(handle, name, argCount, argTypes) {
- requireHandle(handle);
- name = getStringOrSymbol(name);
-
- var args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 4));
- var obj = _emval_handle_array[handle].value;
- var rv = obj[name].apply(obj, args);
- return __emval_register(rv);
+function lookupTypes(argCount, argTypes, argWireTypes) {
+ var a = new Array(argCount);
+ for (var i = 0; i < argCount; ++i) {
+ a[i] = requireRegisteredType(
+ HEAP32[(argTypes >> 2) + i],
+ "parameter " + i);
+ }
+ return a;
}
-function __emval_call_void_method(handle, name, argCount, argTypes) {
- requireHandle(handle);
- name = getStringOrSymbol(name);
+function __emval_get_method_caller(argCount, argTypes) {
+ var types = lookupTypes(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 args = parseParameters(
- argCount,
- argTypes,
- Array.prototype.slice.call(arguments, 4));
- var obj = _emval_handle_array[handle].value;
- obj[name].apply(obj, args);
+ var argsList = ""; // 'arg0, arg1, arg2, ... , argN'
+ var argsListWired = ""; // 'arg0Wired, ..., argNWired'
+ for (var i = 0; i < argCount - 1; ++i) {
+ argsList += (i !== 0 ? ", " : "") + "arg" + i;
+ argsListWired += ", arg" + i + "Wired";
+ args1.push("argType" + i);
+ args2.push(types[1 + i]);
+ }
+
+ var invokerFnBody =
+ "return Runtime.addFunction(createNamedFunction('" + signatureName + "', function (handle, name" + argsListWired + ") {\n" +
+ "requireHandle(handle);\n" +
+ "name = getStringOrSymbol(name);\n";
+
+ for (var i = 0; i < argCount - 1; ++i) {
+ invokerFnBody += "var arg" + i + " = argType" + i + ".fromWireType(arg" + i + "Wired);\n";
+ }
+ invokerFnBody +=
+ "var obj = _emval_handle_array[handle].value;\n" +
+ "return retType.toWireType(null, obj[name](" + argsList + "));\n" +
+ "}));\n";
+
+ args1.push(invokerFnBody);
+ var invokerFunction = new_(Function, args1).apply(null, args2);
+ return invokerFunction;
}
function __emval_has_function(handle, name) {
diff --git a/src/intertyper.js b/src/intertyper.js
index 57e3011d..445c37f4 100644
--- a/src/intertyper.js
+++ b/src/intertyper.js
@@ -336,6 +336,8 @@ function intertyper(data, sidePass, baseLineNums) {
return 'InsertValue';
if (tokensLength >= 3 && token0Text == 'phi')
return 'Phi';
+ if (tokensLength >= 3 && token0Text == 'va_arg')
+ return 'va_arg';
if (tokensLength >= 3 && token0Text == 'landingpad')
return 'Landingpad';
if (token0Text == 'fence')
@@ -817,6 +819,16 @@ function intertyper(data, sidePass, baseLineNums) {
this.forwardItem(item, 'Reintegrator');
}
});
+ // 'phi'
+ substrate.addActor('va_arg', {
+ processItem: function(item) {
+ item.intertype = 'va_arg';
+ var segments = splitTokenList(item.tokens.slice(1));
+ item.type = segments[1][0].text;
+ item.value = parseLLVMSegment(segments[0]);
+ this.forwardItem(item, 'Reintegrator');
+ }
+ });
// mathops
substrate.addActor('Mathops', {
processItem: function(item) {
diff --git a/src/jsifier.js b/src/jsifier.js
index 32c224b7..8ab96a25 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -486,11 +486,18 @@ function JSify(data, functionsOnly, givenFunctions) {
item.JS = '';
} else {
// If this is not linkable, anything not in the library is definitely missing
+ var cancel = false;
if (!LINKABLE && !LibraryManager.library.hasOwnProperty(shortident) && !LibraryManager.library.hasOwnProperty(shortident + '__inline')) {
+ if (ERROR_ON_UNDEFINED_SYMBOLS) error('unresolved symbol: ' + shortident);
if (VERBOSE || WARN_ON_UNDEFINED_SYMBOLS) printErr('warning: unresolved symbol: ' + shortident);
- LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
+ if (ASM_JS || item.ident in DEAD_FUNCTIONS) {
+ // emit a stub that will fail during runtime. this allows asm validation to succeed.
+ LibraryManager.library[shortident] = new Function("Module['printErr']('missing function: " + shortident + "'); abort(-1);");
+ } else {
+ cancel = true; // emit nothing, not even var X = undefined;
+ }
}
- item.JS = addFromLibrary(shortident);
+ item.JS = cancel ? ';' : addFromLibrary(shortident);
}
return ret;
}
@@ -1174,6 +1181,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (disabled) {
ret = call_ + ';';
} else if (ASM_JS) {
+ call_ = call_.replace('; return', ''); // we auto-add returns when aborting, but do not need them here
ret = '(__THREW__ = 0,' + call_ + ');';
} else {
ret = '(function() { try { __THREW__ = 0; return '
@@ -1224,7 +1232,7 @@ function JSify(data, functionsOnly, givenFunctions) {
if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) {
ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;';
item.assignTo = null;
- if (ASSERTIONS) warnOnce('landingpad, but exceptions are disabled!');
+ if (VERBOSE) warnOnce('landingpad, but exceptions are disabled!');
return ret;
}
var catchTypeArray = item.catchables.map(finalizeLLVMParameter).map(function(element) { return asmCoercion(element, 'i32') }).join(',');
@@ -1294,6 +1302,14 @@ function JSify(data, functionsOnly, givenFunctions) {
return RuntimeGenerator.stackAlloc(getFastValue(calcAllocatedSize(item.allocatedType), '*', item.allocatedNum));
}
});
+ makeFuncLineActor('va_arg', function(item) {
+ assert(TARGET_LE32);
+ var ident = item.value.ident;
+ var move = Runtime.STACK_ALIGN;
+ return '(tempInt=' + makeGetValue(ident, 4, '*') + ',' +
+ makeSetValue(ident, 4, 'tempInt + ' + move, '*') + ',' +
+ makeGetValue(makeGetValue(ident, 0, '*'), 'tempInt', item.type) + ')';
+ });
makeFuncLineActor('mathop', processMathop);
@@ -1317,16 +1333,21 @@ function JSify(data, functionsOnly, givenFunctions) {
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
var simpleIdent = shortident;
- var callIdent = LibraryManager.getRootIdent(simpleIdent);
- if (callIdent) {
- simpleIdent = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is
- if (callIdent.indexOf('.') < 0) {
- callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix
- }
+ if (isLocalVar(ident)) {
+ var callIdent = ident;
} else {
- callIdent = ident;
+ // Not a local var, check if in library
+ var callIdent = LibraryManager.getRootIdent(simpleIdent);
+ if (callIdent) {
+ simpleIdent = callIdent; // ident may not be in library, if all there is is ident__inline, but in this case it is
+ if (callIdent.indexOf('.') < 0) {
+ callIdent = '_' + callIdent; // Not Math.*, so add the normal prefix
+ }
+ } else {
+ callIde