summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/embind/embind.js436
-rw-r--r--src/embind/emval.js21
-rw-r--r--src/emscripten-source-map.min.js3
-rw-r--r--src/jsifier.js10
-rw-r--r--src/library.js323
-rw-r--r--src/library_browser.js146
-rw-r--r--src/library_fs.js77
-rw-r--r--src/library_gl.js47
-rw-r--r--src/library_glfw.js2
-rw-r--r--src/library_html5.js6
-rw-r--r--src/library_sdl.js394
-rw-r--r--src/modules.js1
-rw-r--r--src/parseTools.js19
-rw-r--r--src/preamble.js236
-rw-r--r--src/relooper/Relooper.cpp122
-rw-r--r--src/relooper/Relooper.h14
-rw-r--r--src/relooper/fuzzer.py6
-rw-r--r--src/relooper/test.cpp31
-rw-r--r--src/relooper/test.txt153
-rw-r--r--src/runtime.js13
-rw-r--r--src/settings.js18
-rw-r--r--src/shell.html18
-rw-r--r--src/shell.js6
-rw-r--r--src/shell_minimal.html18
-rw-r--r--src/simd.js1643
-rw-r--r--src/struct_info.json18
26 files changed, 2314 insertions, 1467 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index 6ec07cd9..8c8d73ad 100644
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -1,13 +1,15 @@
-/*global Module*/
+/*global Module, asm*/
/*global _malloc, _free, _memcpy*/
/*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64*/
/*global readLatin1String*/
/*global __emval_register, _emval_handle_array, __emval_decref*/
/*global ___getTypeName*/
+/*global requireHandle*/
/*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 PureVirtualError = Module['PureVirtualError'] = extendError(BindingError, 'PureVirtualError');
function throwInternalError(message) {
throw new InternalError(message);
@@ -151,6 +153,59 @@ function _embind_repr(v) {
}
}
+// raw pointer -> instance
+var registeredInstances = {};
+
+function getBasestPointer(class_, ptr) {
+ if (ptr === undefined) {
+ throwBindingError('ptr should not be undefined');
+ }
+ while (class_.baseClass) {
+ ptr = class_.upcast(ptr);
+ class_ = class_.baseClass;
+ }
+ return ptr;
+}
+
+function registerInheritedInstance(class_, ptr, instance) {
+ ptr = getBasestPointer(class_, ptr);
+ if (registeredInstances.hasOwnProperty(ptr)) {
+ throwBindingError('Tried to register registered instance: ' + ptr);
+ } else {
+ registeredInstances[ptr] = instance;
+ }
+}
+
+function unregisterInheritedInstance(class_, ptr) {
+ ptr = getBasestPointer(class_, ptr);
+ if (registeredInstances.hasOwnProperty(ptr)) {
+ delete registeredInstances[ptr];
+ } else {
+ throwBindingError('Tried to unregister unregistered instance: ' + ptr);
+ }
+}
+
+function getInheritedInstance(class_, ptr) {
+ ptr = getBasestPointer(class_, ptr);
+ return registeredInstances[ptr];
+}
+
+function getInheritedInstanceCount() {
+ return Object.keys(registeredInstances).length;
+}
+Module['getInheritedInstanceCount'] = getInheritedInstanceCount;
+
+function getLiveInheritedInstances() {
+ var rv = [];
+ for (var k in registeredInstances) {
+ if (registeredInstances.hasOwnProperty(k)) {
+ rv.push(registeredInstances[k]);
+ }
+ }
+ return rv;
+}
+Module['getLiveInheritedInstances'] = getLiveInheritedInstances;
+
// typeID -> { toWireType: ..., fromWireType: ... }
var registeredTypes = {};
@@ -535,6 +590,9 @@ function __embind_register_emval(rawType, name) {
'argPackAdvance': 8,
'readValueFromPointer': simpleReadValueFromPointer,
destructorFunction: null, // This type does not need a destructor
+
+ // TODO: do we need a deleteObject here? write a test where
+ // emval is passed into JS via an interface
});
}
@@ -622,10 +680,6 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
var isClassMethodFunc = (argTypes[1] !== null && classType !== null);
- if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) {
- throwBindingError('Global function '+humanName+' is not defined!');
- }
-
// Free functions with signature "void function()" do not need an invoker that marshalls between wire types.
// TODO: This omits argument count check - enable only at -O3 or similar.
// if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) {
@@ -634,7 +688,7 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
var argsList = "";
var argsListWired = "";
- for(var i = 0; i < argCount-2; ++i) {
+ for(var i = 0; i < argCount - 2; ++i) {
argsList += (i!==0?", ":"")+"arg"+i;
argsListWired += (i!==0?", ":"")+"arg"+i+"Wired";
}
@@ -662,14 +716,14 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
}
var dtorStack = needsDestructorStack ? "destructors" : "null";
- var args1 = ["throwBindingError", "classType", "invoker", "fn", "runDestructors", "retType", "classParam"];
- var args2 = [throwBindingError, classType, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
+ var args1 = ["throwBindingError", "invoker", "fn", "runDestructors", "retType", "classParam"];
+ var args2 = [throwBindingError, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
if (isClassMethodFunc) {
invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n";
}
- for(var i = 0; i < argCount-2; ++i) {
+ for(var i = 0; i < argCount - 2; ++i) {
invokerFnBody += "var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";
args1.push("argType"+i);
args2.push(argTypes[i+2]);
@@ -688,7 +742,7 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
invokerFnBody += "runDestructors(destructors);\n";
} else {
for(var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method.
- var paramName = (i === 1 ? "thisWired" : ("arg"+(i-2)+"Wired"));
+ var paramName = (i === 1 ? "thisWired" : ("arg"+(i - 2)+"Wired"));
if (argTypes[i].destructorFunction !== null) {
invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";
args1.push(paramName+"_dtor");
@@ -708,10 +762,50 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp
return invokerFunction;
}
-function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) {
+function requireFunction(signature, rawFunction) {
+ signature = readLatin1String(signature);
+ var fp;
+ // asm.js does not define FUNCTION_TABLE
+ if (typeof FUNCTION_TABLE === "undefined") {
+ // asm.js does not give direct access to the function tables,
+ // and thus we must go through the dynCall interface which allows
+ // calling into a signature's function table by pointer value.
+ //
+ // https://github.com/dherman/asm.js/issues/83
+ //
+ // This has three main penalties:
+ // - dynCall is another function call in the path from JavaScript to C++.
+ // - JITs may not predict through the function table indirection at runtime.
+ // - Function.prototype.bind generally benchmarks poorly relative to
+ // function objects, but using 'arguments' would confound JITs and
+ // possibly allocate.
+ var dc = asm['dynCall_' + signature];
+ if (dc === undefined) {
+ // We will always enter this branch if the signature
+ // contains 'f' and PRECISE_F32 is not enabled.
+ //
+ // Try again, replacing 'f' with 'd'.
+ dc = asm['dynCall_' + signature.replace(/f/g, 'd')];
+ if (dc === undefined) {
+ throwBindingError("No dynCall invoker for signature: " + signature);
+ }
+ }
+ fp = dc.bind(undefined, rawFunction);
+ } else {
+ fp = FUNCTION_TABLE[rawFunction];
+ }
+
+ if (typeof fp !== "function") {
+ throwBindingError("unknown function pointer with signature " + signature + ": " + rawFunction);
+ }
+ return fp;
+}
+
+function __embind_register_function(name, argCount, rawArgTypesAddr, signature, rawInvoker, fn) {
var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
name = readLatin1String(name);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
+
+ rawInvoker = requireFunction(signature, rawInvoker);
exposePublicSymbol(name, function() {
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
@@ -726,11 +820,11 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
var tupleRegistrations = {};
-function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) {
+function __embind_register_value_array(rawType, name, constructorSignature, rawConstructor, destructorSignature, rawDestructor) {
tupleRegistrations[rawType] = {
name: readLatin1String(name),
- rawConstructor: FUNCTION_TABLE[rawConstructor],
- rawDestructor: FUNCTION_TABLE[rawDestructor],
+ rawConstructor: requireFunction(constructorSignature, rawConstructor),
+ rawDestructor: requireFunction(destructorSignature, rawDestructor),
elements: [],
};
}
@@ -738,18 +832,20 @@ function __embind_register_value_array(rawType, name, rawConstructor, rawDestruc
function __embind_register_value_array_element(
rawTupleType,
getterReturnType,
+ getterSignature,
getter,
getterContext,
setterArgumentType,
+ setterSignature,
setter,
setterContext
) {
tupleRegistrations[rawTupleType].elements.push({
getterReturnType: getterReturnType,
- getter: FUNCTION_TABLE[getter],
+ getter: requireFunction(getterSignature, getter),
getterContext: getterContext,
setterArgumentType: setterArgumentType,
- setter: FUNCTION_TABLE[setter],
+ setter: requireFunction(setterSignature, setter),
setterContext: setterContext,
});
}
@@ -818,13 +914,15 @@ var structRegistrations = {};
function __embind_register_value_object(
rawType,
name,
+ constructorSignature,
rawConstructor,
+ destructorSignature,
rawDestructor
) {
structRegistrations[rawType] = {
name: readLatin1String(name),
- rawConstructor: FUNCTION_TABLE[rawConstructor],
- rawDestructor: FUNCTION_TABLE[rawDestructor],
+ rawConstructor: requireFunction(constructorSignature, rawConstructor),
+ rawDestructor: requireFunction(destructorSignature, rawDestructor),
fields: [],
};
}
@@ -833,19 +931,21 @@ function __embind_register_value_object_field(
structType,
fieldName,
getterReturnType,
+ getterSignature,
getter,
getterContext,
setterArgumentType,
+ setterSignature,
setter,
setterContext
) {
structRegistrations[structType].fields.push({
fieldName: readLatin1String(fieldName),
getterReturnType: getterReturnType,
- getter: FUNCTION_TABLE[getter],
+ getter: requireFunction(getterSignature, getter),
getterContext: getterContext,
setterArgumentType: setterArgumentType,
- setter: FUNCTION_TABLE[setter],
+ setter: requireFunction(setterSignature, setter),
setterContext: setterContext,
});
}
@@ -1082,14 +1182,14 @@ function RegisteredPointer(
}
}
-RegisteredPointer.prototype.getPointee = function(ptr) {
+RegisteredPointer.prototype.getPointee = function getPointee(ptr) {
if (this.rawGetPointee) {
ptr = this.rawGetPointee(ptr);
}
return ptr;
};
-RegisteredPointer.prototype.destructor = function(ptr) {
+RegisteredPointer.prototype.destructor = function destructor(ptr) {
if (this.rawDestructor) {
this.rawDestructor(ptr);
}
@@ -1098,7 +1198,13 @@ RegisteredPointer.prototype.destructor = function(ptr) {
RegisteredPointer.prototype['argPackAdvance'] = 8;
RegisteredPointer.prototype['readValueFromPointer'] = simpleReadValueFromPointer;
-RegisteredPointer.prototype['fromWireType'] = function(ptr) {
+RegisteredPointer.prototype['deleteObject'] = function deleteObject(handle) {
+ if (handle !== null) {
+ handle['delete']();
+ }
+};
+
+RegisteredPointer.prototype['fromWireType'] = function fromWireType(ptr) {
// ptr is a raw pointer (or a raw smartpointer)
// rawPointer is a maybe-null raw pointer
@@ -1108,6 +1214,22 @@ RegisteredPointer.prototype['fromWireType'] = function(ptr) {
return null;
}
+ var registeredInstance = getInheritedInstance(this.registeredClass, rawPointer);
+ if (undefined !== registeredInstance) {
+ // JS object has been neutered, time to repopulate it
+ if (0 === registeredInstance.$$.count.value) {
+ registeredInstance.$$.ptr = rawPointer;
+ registeredInstance.$$.smartPtr = ptr;
+ return registeredInstance['clone']();
+ } else {
+ // else, just increment reference count on existing object
+ // it already has a reference to the smart pointer
+ var rv = registeredInstance['clone']();
+ this.destructor(ptr);
+ return rv;
+ }
+ }
+
function makeDefaultHandle() {
if (this.isSmartPointer) {
return makeClassHandle(this.registeredClass.instancePrototype, {
@@ -1183,7 +1305,7 @@ function getInstanceTypeName(handle) {
return handle.$$.ptrType.registeredClass.name;
}
-ClassHandle.prototype['isAliasOf'] = function(other) {
+ClassHandle.prototype['isAliasOf'] = function isAliasOf(other) {
if (!(this instanceof ClassHandle)) {
return false;
}
@@ -1213,19 +1335,24 @@ function throwInstanceAlreadyDeleted(obj) {
throwBindingError(getInstanceTypeName(obj) + ' instance already deleted');
}
-ClassHandle.prototype['clone'] = function() {
+ClassHandle.prototype['clone'] = function clone() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- var clone = Object.create(Object.getPrototypeOf(this), {
- $$: {
- value: shallowCopy(this.$$),
- }
- });
+ if (this.$$.preservePointerOnDelete) {
+ this.$$.count.value += 1;
+ return this;
+ } else {
+ var clone = Object.create(Object.getPrototypeOf(this), {
+ $$: {
+ value: shallowCopy(this.$$),
+ }
+ });
- clone.$$.count.value += 1;
- return clone;
+ clone.$$.count.value += 1;
+ return clone;
+ }
};
function runDestructor(handle) {
@@ -1241,16 +1368,20 @@ ClassHandle.prototype['delete'] = function ClassHandle_delete() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- if (this.$$.deleteScheduled) {
+
+ if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
throwBindingError('Object already scheduled for deletion');
}
this.$$.count.value -= 1;
- if (0 === this.$$.count.value) {
+ var toDelete = 0 === this.$$.count.value;
+ if (toDelete) {
runDestructor(this);
}
- this.$$.smartPtr = undefined;
- this.$$.ptr = undefined;
+ if (!this.$$.preservePointerOnDelete) {
+ this.$$.smartPtr = undefined;
+ this.$$.ptr = undefined;
+ }
};
var deletionQueue = [];
@@ -1263,7 +1394,7 @@ ClassHandle.prototype['deleteLater'] = function deleteLater() {
if (!this.$$.ptr) {
throwInstanceAlreadyDeleted(this);
}
- if (this.$$.deleteScheduled) {
+ if (this.$$.deleteScheduled && !this.$$.preservePointerOnDelete) {
throwBindingError('Object already scheduled for deletion');
}
deletionQueue.push(this);
@@ -1309,12 +1440,15 @@ function RegisteredClass(
this.getActualType = getActualType;
this.upcast = upcast;
this.downcast = downcast;
+ this.pureVirtualFunctions = [];
}
function shallowCopy(o) {
var rv = {};
for (var k in o) {
- rv[k] = o[k];
+ if (Object.prototype.hasOwnProperty.call(o, k)) {
+ rv[k] = o[k];
+ }
}
return rv;
}
@@ -1324,17 +1458,25 @@ function __embind_register_class(
rawPointerType,
rawConstPointerType,
baseClassRawType,
+ getActualTypeSignature,
getActualType,
+ upcastSignature,
upcast,
+ downcastSignature,
downcast,
name,
+ destructorSignature,
rawDestructor
) {
name = readLatin1String(name);
- rawDestructor = FUNCTION_TABLE[rawDestructor];
- getActualType = FUNCTION_TABLE[getActualType];
- upcast = FUNCTION_TABLE[upcast];
- downcast = FUNCTION_TABLE[downcast];
+ getActualType = requireFunction(getActualTypeSignature, getActualType);
+ if (upcast) {
+ upcast = requireFunction(upcastSignature, upcast);
+ }
+ if (downcast) {
+ downcast = requireFunction(downcastSignature, downcast);
+ }
+ rawDestructor = requireFunction(destructorSignature, rawDestructor);
var legalFunctionName = makeLegalFunctionName(name);
exposePublicSymbol(legalFunctionName, function() {
@@ -1424,11 +1566,12 @@ function __embind_register_class_constructor(
rawClassType,
argCount,
rawArgTypesAddr,
+ invokerSignature,
invoker,
rawConstructor
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- invoker = FUNCTION_TABLE[invoker];
+ invoker = requireFunction(invokerSignature, invoker);
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
@@ -1440,12 +1583,12 @@ function __embind_register_class_constructor(
if (undefined !== classType.registeredClass.constructor_body[argCount - 1]) {
throw new BindingError("Cannot register multiple constructors with identical number of parameters (" + (argCount-1) + ") for class '" + classType.name + "'! Overload resolution is currently only performed using the parameter count, not actual type info!");
}
- classType.registeredClass.constructor_body[argCount - 1] = function() {
+ classType.registeredClass.constructor_body[argCount - 1] = function unboundTypeHandler() {
throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes);
};
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- classType.registeredClass.constructor_body[argCount - 1] = function() {
+ classType.registeredClass.constructor_body[argCount - 1] = function constructor_body() {
if (arguments.length !== argCount - 1) {
throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
}
@@ -1474,9 +1617,12 @@ function downcastPointer(ptr, ptrClass, desiredClass) {
if (undefined === desiredClass.baseClass) {
return null; // no conversion
}
- // O(depth) stack space used
- return desiredClass.downcast(
- downcastPointer(ptr, ptrClass, desiredClass.baseClass));
+
+ var rv = downcastPointer(ptr, ptrClass, desiredClass.baseClass);
+ if (rv === null) {
+ return null;
+ }
+ return desiredClass.downcast(rv);
}
function upcastPointer(ptr, ptrClass, desiredClass) {
@@ -1513,32 +1659,38 @@ function __embind_register_class_function(
methodName,
argCount,
rawArgTypesAddr, // [ReturnType, ThisType, Args...]
+ invokerSignature,
rawInvoker,
- context
+ context,
+ isPureVirtual
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
methodName = readLatin1String(methodName);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
+ rawInvoker = requireFunction(invokerSignature, rawInvoker);
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
var humanName = classType.name + '.' + methodName;
- var unboundTypesHandler = function() {
+ if (isPureVirtual) {
+ classType.registeredClass.pureVirtualFunctions.push(methodName);
+ }
+
+ function unboundTypesHandler() {
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
+ }
var proto = classType.registeredClass.instancePrototype;
var method = proto[methodName];
- if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount-2)) {
+ if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount - 2)) {
// This is the first overload to be registered, OR we are replacing a function in the base class with a function in the derived class.
- unboundTypesHandler.argCount = argCount-2;
+ unboundTypesHandler.argCount = argCount - 2;
unboundTypesHandler.className = classType.name;
proto[methodName] = unboundTypesHandler;
} else {
// There was an existing function with the same name registered. Set up a function overload routing table.
ensureOverloadTable(proto, methodName, humanName);
- proto[methodName].overloadTable[argCount-2] = unboundTypesHandler;
+ proto[methodName].overloadTable[argCount - 2] = unboundTypesHandler;
}
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
@@ -1550,7 +1702,7 @@ function __embind_register_class_function(
if (undefined === proto[methodName].overloadTable) {
proto[methodName] = memberFunction;
} else {
- proto[methodName].overloadTable[argCount-2] = memberFunction;
+ proto[methodName].overloadTable[argCount - 2] = memberFunction;
}
return [];
@@ -1559,64 +1711,20 @@ function __embind_register_class_function(
});
}
-function __embind_register_class_class_function(
- rawClassType,
- methodName,
- argCount,
- rawArgTypesAddr,
- rawInvoker,
- fn
-) {
- var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- methodName = readLatin1String(methodName);
- rawInvoker = FUNCTION_TABLE[rawInvoker];
- whenDependentTypesAreResolved([], [rawClassType], function(classType) {
- classType = classType[0];
- var humanName = classType.name + '.' + methodName;
-
- var unboundTypesHandler = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
-
- var proto = classType.registeredClass.constructor;
- if (undefined === proto[methodName]) {
- // This is the first function to be registered with this name.
- unboundTypesHandler.argCount = argCount-1;
- proto[methodName] = unboundTypesHandler;
- } else {
- // There was an existing function with the same name registered. Set up a function overload routing table.
- ensureOverloadTable(proto, methodName, humanName);
- proto[methodName].overloadTable[argCount-1] = unboundTypesHandler;
- }
-
- whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered,
- // the function handlers go into an overload table.
- var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
- var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn);
- if (undefined === proto[methodName].overloadTable) {
- proto[methodName] = func;
- } else {
- proto[methodName].overloadTable[argCount-1] = func;
- }
- return [];
- });
- return [];
- });
-}
-
function __embind_register_class_property(
classType,
fieldName,
getterReturnType,
+ getterSignature,
getter,
getterContext,
setterArgumentType,
+ setterSignature,
setter,
setterContext
) {
fieldName = readLatin1String(fieldName);
- getter = FUNCTION_TABLE[getter];
+ getter = requireFunction(getterSignature, getter);
whenDependentTypesAreResolved([], [classType], function(classType) {
classType = classType[0];
@@ -1654,7 +1762,7 @@ function __embind_register_class_property(
};
if (setter) {
- setter = FUNCTION_TABLE[setter];
+ setter = requireFunction(setterSignature, setter);
var setterArgumentType = types[1];
desc.set = function(v) {
var ptr = validateThis(this, classType, humanName + ' setter');
@@ -1672,6 +1780,112 @@ function __embind_register_class_property(
});
}
+function __embind_register_class_class_function(
+ rawClassType,
+ methodName,
+ argCount,
+ rawArgTypesAddr,
+ invokerSignature,
+ rawInvoker,
+ fn
+) {
+ var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
+ methodName = readLatin1String(methodName);
+ rawInvoker = requireFunction(invokerSignature, rawInvoker);
+ whenDependentTypesAreResolved([], [rawClassType], function(classType) {
+ classType = classType[0];
+ var humanName = classType.name + '.' + methodName;
+
+ function unboundTypesHandler() {
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ }
+
+ var proto = classType.registeredClass.constructor;
+ if (undefined === proto[methodName]) {
+ // This is the first function to be registered with this name.
+ unboundTypesHandler.argCount = argCount-1;
+ proto[methodName] = unboundTypesHandler;
+ } else {
+ // There was an existing function with the same name registered. Set up a function overload routing table.
+ ensureOverloadTable(proto, methodName, humanName);
+ proto[methodName].overloadTable[argCount-1] = unboundTypesHandler;
+ }
+
+ whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
+ // Replace the initial unbound-types-handler stub with the proper function. If multiple overloads are registered,
+ // the function handlers go into an overload table.
+ var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
+ var func = craftInvokerFunction(humanName, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn);
+ if (undefined === proto[methodName].overloadTable) {
+ proto[methodName] = func;
+ } else {
+ proto[methodName].overloadTable[argCount-1] = func;
+ }
+ return [];
+ });
+ return [];
+ });
+}
+
+function __embind_create_inheriting_constructor(constructorName, wrapperType, properties) {
+ constructorName = readLatin1String(constructorName);
+ wrapperType = requireRegisteredType(wrapperType, 'wrapper');
+ properties = requireHandle(properties);
+
+ var arraySlice = [].slice;
+
+ var registeredClass = wrapperType.registeredClass;
+ var wrapperPrototype = registeredClass.instancePrototype;
+ var baseClass = registeredClass.baseClass;
+ var baseClassPrototype = baseClass.instancePrototype;
+ var baseConstructor = registeredClass.baseClass.constructor;
+ var ctor = createNamedFunction(constructorName, function() {
+ registeredClass.baseClass.pureVirtualFunctions.forEach(function(name) {
+ if (this[name] === baseClassPrototype[name]) {
+ throw new PureVirtualError('Pure virtual function ' + name + ' must be implemented in JavaScript');
+ }
+ }.bind(this));
+
+ Object.defineProperty(this, '__parent', {
+ value: wrapperPrototype
+ });
+ this.__construct.apply(this, arraySlice.call(arguments));
+ });
+
+ // It's a little nasty that we're modifying the wrapper prototype here.
+
+ wrapperPrototype.__construct = function __construct() {
+ if (this === wrapperPrototype) {
+ throwBindingError("Pass correct 'this' to __construct");
+ }
+
+ var inner = baseConstructor.implement.apply(
+ undefined,
+ [this].concat(arraySlice.call(arguments)));
+ var $$ = inner.$$;
+ inner.notifyOnDestruction();
+ $$.preservePointerOnDelete = true;
+ Object.defineProperty(this, '$$', {
+ value: $$
+ });
+ registerInheritedInstance(registeredClass, $$.ptr, this);
+ };
+
+ wrapperPrototype.__destruct = function __destruct() {
+ if (this === wrapperPrototype) {
+ throwBindingError("Pass correct 'this' to __destruct");
+ }
+
+ unregisterInheritedInstance(registeredClass, this.$$.ptr);
+ };
+
+ ctor.prototype = Object.create(wrapperPrototype);
+ for (var p in properties) {
+ ctor.prototype[p] = properties[p];
+ }
+ return __emval_register(ctor);
+}
+
var char_0 = '0'.charCodeAt(0);
var char_9 = '9'.charCodeAt(0);
function makeLegalFunctionName(name) {
@@ -1689,16 +1903,20 @@ function __embind_register_smart_ptr(
rawPointeeType,
name,
sharingPolicy,
+ getPointeeSignature,
rawGetPointee,
+ constructorSignature,
rawConstructor,
+ shareSignature,
rawShare,
+ destructorSignature,
rawDestructor
) {
name = readLatin1String(name);
- rawGetPointee = FUNCTION_TABLE[rawGetPointee];
- rawConstructor = FUNCTION_TABLE[rawConstructor];
- rawShare = FUNCTION_TABLE[rawShare];
- rawDestructor = FUNCTION_TABLE[rawDestructor];
+ rawGetPointee = requireFunction(getPointeeSignature, rawGetPointee);
+ rawConstructor = requireFunction(constructorSignature, rawConstructor);
+ rawShare = requireFunction(shareSignature, rawShare);
+ rawDestructor = requireFunction(destructorSignature, rawDestructor);
whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) {
pointeeType = pointeeType[0];
diff --git a/src/embind/emval.js b/src/embind/emval.js
index 4007701a..1661bc02 100644
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -265,7 +265,14 @@ function __emval_get_method_caller(argCount, argTypes) {
" args += argType" + i + ".argPackAdvance;\n";
}
functionBody +=
- " var rv = handle[name](" + argsList + ");\n" +
+ " var rv = handle[name](" + argsList + ");\n";
+ for (var i = 0; i < argCount - 1; ++i) {
+ if (types[i + 1]['deleteObject']) {
+ functionBody +=
+ " argType" + i + ".deleteObject(arg" + i + ");\n";
+ }
+ }
+ functionBody +=
" return retType.toWireType(destructors, rv);\n" +
"};\n";
@@ -281,8 +288,16 @@ function __emval_call_method(caller, handle, methodName, destructorsRef, args) {
return caller(handle, methodName, allocateDestructors(destructorsRef), args);
}
-function __emval_has_function(handle, name) {
+function __emval_has_function(handle, name, classType) {
handle = requireHandle(handle);
name = getStringOrSymbol(name);
- return handle[name] instanceof Function;
+ classType = requireRegisteredType(classType, 'class wrapper filter');
+
+ var filter = classType.registeredClass.instancePrototype[name];
+ return (handle[name] instanceof Function) && (filter === undefined || handle[name] !== filter);
+}
+
+function __emval_typeof(handle) {
+ handle = requireHandle(handle);
+ return __emval_register(typeof handle);
}
diff --git a/src/emscripten-source-map.min.js b/src/emscripten-source-map.min.js
index 9151400f..44cb1d84 100644
--- a/src/emscripten-source-map.min.js
+++ b/src/emscripten-source-map.min.js
@@ -4,7 +4,7 @@ var emscripten_sourcemap_xmlHttp = undefined;
function emscripten_sourceMapLoaded() {
if (emscripten_sourcemap_xmlHttp.readyState === 4) {
Module['removeRunDependency']('sourcemap');
- if (emscripten_sourcemap_xmlHttp.status === 200) {
+ if (emscripten_sourcemap_xmlHttp.status === 200 || emscripten_sourcemap_xmlHttp.status === 0) {
emscripten_source_map = new window.sourceMap.SourceMapConsumer(emscripten_sourcemap_xmlHttp.responseText);
console.log('Source map data loaded.');
} else {
@@ -20,6 +20,7 @@ function emscripten_loadSourceMap() {
emscripten_sourcemap_xmlHttp = new XMLHttpRequest();
emscripten_sourcemap_xmlHttp.onreadystatechange = emscripten_sourceMapLoaded;
emscripten_sourcemap_xmlHttp.open("GET", url, true);
+ emscripten_sourcemap_xmlHttp.responseType = "text";
emscripten_sourcemap_xmlHttp.send(null);
}
diff --git a/src/jsifier.js b/src/jsifier.js
index 065c66a8..791273f4 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1321,10 +1321,10 @@ function JSify(data, functionsOnly) {
// vector load
var native = getVectorNativeType(item.valueType);
var base = getSIMDName(native);
- return base + '32x4(' + makeGetValue(value, 0, native, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 4, native, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 8, native, 0, item.unsigned, 0, item.align) + ',' +
- makeGetValue(value, 12, native, 0, item.unsigned, 0, item.align) + ');';
+ return 'SIMD.' + base + '32x4(' + makeGetValue(value, 0, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 4, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 8, native, 0, item.unsigned, 0, item.align) + ',' +
+ makeGetValue(value, 12, native, 0, item.unsigned, 0, item.align) + ');';
}
var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED;
switch (impl) {
@@ -1395,7 +1395,7 @@ function JSify(data, functionsOnly) {
}
for (var i = 0; i < 4; i++) assert(mask[0] == 0 || mask == 1);
i = 0;
- return base + '32x4(' + mask.map(function(m) {
+ return 'SIMD.' + base + '32x4(' + mask.map(function(m) {
return (m == 1 ? second : first) + '.' + simdLane[i++];
}).join(',') + ')';
}
diff --git a/src/library.js b/src/library.js
index 1d5a9140..c17952b3 100644
--- a/src/library.js
+++ b/src/library.js
@@ -762,12 +762,18 @@ LibraryManager.library = {
// http://pubs.opengroup.org/onlinepubs/000095399/functions/crypt.html
// TODO: Implement (probably compile from C).
___setErrNo(ERRNO_CODES.ENOSYS);
+#if ASSERTIONS
+ Runtime.warnOnce('crypt() returning an error as we do not support it');
+#endif
return 0;
},
encrypt: function(block, edflag) {
// void encrypt(char block[64], int edflag);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html
// TODO: Implement (probably compile from C).
+#if ASSERTIONS
+ Runtime.warnOnce('encrypt() returning an error as we do not support it');
+#endif
___setErrNo(ERRNO_CODES.ENOSYS);
},
fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'],
@@ -940,6 +946,9 @@ LibraryManager.library = {
// It is possible to implement this using two device streams, but pipes make
// little sense in a single-threaded environment, so we do not support them.
___setErrNo(ERRNO_CODES.ENOSYS);
+#if ASSERTIONS
+ Runtime.warnOnce('pipe() returning an error as we do not support them');
+#endif
return -1;
},
pread__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
@@ -1584,7 +1593,6 @@ LibraryManager.library = {
return /^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?/.exec(text);
},
- // TODO: Document.
_scanString__deps: ['_getFloat'],
_scanString: function(format, get, unget, varargs) {
if (!__scanString.whiteSpace) {
@@ -1726,6 +1734,7 @@ LibraryManager.library = {
}
var long_ = false;
var half = false;
+ var quarter = false;
var longLong = false;
if (format[formatIndex] == 'l') {
long_ = true;
@@ -1737,6 +1746,10 @@ LibraryManager.library = {
} else if (format[formatIndex] == 'h') {
half = true;
formatIndex++;
+ if (format[formatIndex] == 'h') {
+ quarter = true;
+ formatIndex++;
+ }
}
var type = format[formatIndex];
formatIndex++;
@@ -1795,20 +1808,21 @@ LibraryManager.library = {
var text = buffer.join('');
var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}};
argIndex += Runtime.getAlignSize('void*', null, true);
+ var base = 10;
switch (type) {
+ case 'X': case 'x':
+ base = 16;
case 'd': case 'u': case 'i':
- if (half) {
- {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}};
+ if (quarter) {
+ {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i8') }}};
+ } else if (half) {
+ {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i16') }}};
} else if (longLong) {
- {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i64') }}};
+ {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i64') }}};
} else {
- {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}};
+ {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i32') }}};
}
break;
- case 'X':
- case 'x':
- {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}};
- break;
case 'F':
case 'f':
case 'E':
@@ -2788,34 +2802,6 @@ LibraryManager.library = {
var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}};
return _fscanf(stdin, format, varargs);
},
- sscanf__deps: ['_scanString'],
- sscanf: function(s, format, varargs) {
- // int sscanf(const char *restrict s, const char *restrict format, ... );
- // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
- var index = 0;
- function get() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; };
- function unget() { index--; };
- return __scanString(format, get, unget, varargs);
- },
- snprintf__deps: ['_formatString', 'malloc'],
- snprintf: function(s, n, format, varargs) {
- // int snprintf(char *restrict s, size_t n, const char *restrict format, ...);
- // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
- var result = __formatString(format, varargs);
- var limit = (n === undefined) ? result.length
- : Math.min(result.length, Math.max(n - 1, 0));
- if (s < 0) {
- s = -s;
- var buf = _malloc(limit+1);
- {{{ makeSetValue('s', '0', 'buf', 'i8*') }}};
- s = buf;
- }
- for (var i = 0; i < limit; i++) {
- {{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}};
- }
- if (limit < n || (n === undefined)) {{{ makeSetValue('s', 'i', '0', 'i8') }}};
- return result.length;
- },
fprintf__deps: ['fwrite', '_formatString'],
fprintf: function(stream, format, varargs) {
// int fprintf(FILE *restrict stream, const char *restrict format, ...);
@@ -2833,16 +2819,6 @@ LibraryManager.library = {
var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}};
return _fprintf(stdout, format, varargs);
},
- sprintf__deps: ['snprintf'],
- sprintf: function(s, format, varargs) {
- // int sprintf(char *restrict s, const char *restrict format, ...);
- // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html
- return _snprintf(s, undefined, format, varargs);
- },
- asprintf__deps: ['sprintf'],
- asprintf: function(s, format, varargs) {
- return _sprintf(-s, format, varargs);
- },
dprintf__deps: ['_formatString', 'write'],
dprintf: function(fd, format, varargs) {
var result = __formatString(format, varargs);
@@ -2854,14 +2830,10 @@ LibraryManager.library = {
#if TARGET_X86
// va_arg is just like our varargs
vfprintf: 'fprintf',
- vsnprintf: 'snprintf',
vprintf: 'printf',
- vsprintf: 'sprintf',
- vasprintf: 'asprintf',
vdprintf: 'dprintf',
vscanf: 'scanf',
vfscanf: 'fscanf',
- vsscanf: 'sscanf',
#endif
#if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN
@@ -2870,22 +2842,10 @@ LibraryManager.library = {
vfprintf: function(s, f, va_arg) {
return _fprintf(s, f, {{{ makeGetValue('va_arg', 0, '*') }}});
},
- vsnprintf__deps: ['snprintf'],
- vsnprintf: function(s, n, format, va_arg) {
- return _snprintf(s, n, format, {{{ makeGetValue('va_arg', 0, '*') }}});
- },
vprintf__deps: ['printf'],
vprintf: function(format, va_arg) {
return _printf(format, {{{ makeGetValue('va_arg', 0, '*') }}});
},
- vsprintf__deps: ['sprintf'],
- vsprintf: function(s, format, va_arg) {
- return _sprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
- },
- vasprintf__deps: ['asprintf'],
- vasprintf: function(s, format, va_arg) {
- return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
- },
vdprintf__deps: ['dprintf'],
vdprintf: function (fd, format, va_arg) {
return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}});
@@ -2898,10 +2858,6 @@ LibraryManager.library = {
vfscanf: function(s, format, va_arg) {
return _fscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
},
- vsscanf__deps: ['sscanf'],
- vsscanf: function(s, format, va_arg) {
- return _sscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}});
- },
#endif
// ==========================================================================
@@ -3208,39 +3164,6 @@ LibraryManager.library = {
{{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}};
},
#endif
- strtoll__deps: ['_parseInt64'],
- strtoll: function(str, endptr, base) {
- return __parseInt64(str, endptr, base, '-9223372036854775808', '9223372036854775807'); // LLONG_MIN, LLONG_MAX.
- },
- strtoll_l__deps: ['strtoll'],
- strtoll_l: function(str, endptr, base) {
- return _strtoll(str, endptr, base); // no locale support yet
- },
- strtol__deps: ['_parseInt'],
- strtol: function(str, endptr, base) {
- return __parseInt(str, endptr, base, -2147483648, 2147483647, 32); // LONG_MIN, LONG_MAX.
- },
- strtol_l__deps: ['strtol'],
- strtol_l: function(str, endptr, base) {
- return _strtol(str, endptr, base); // no locale support yet
- },
- strtoul__deps: ['_parseInt'],
- strtoul: function(str, endptr, base) {
- return __parseInt(str, endptr, base, 0, 4294967295, 32, true); // ULONG_MAX.
- },
- strtoul_l__deps: ['strtoul'],
- strtoul_l: function(str, endptr, base) {
- return _strtoul(str, endptr, base); // no locale support yet
- },
- strtoull__deps: ['_parseInt64'],
- strtoull: function(str, endptr, base) {
- return __parseInt64(str, endptr, base, 0, '18446744073709551615', true); // ULONG_MAX.
- },
- strtoull_l__deps: ['strtoull'],
- strtoull_l: function(str, endptr, base) {
- return _strtoull(str, endptr, base); // no locale support yet
- },
-
environ: 'allocate(1, "i32*", ALLOC_STATIC)',
__environ__deps: ['environ'],
__environ: '_environ',
@@ -3430,6 +3353,7 @@ LibraryManager.library = {
return 0;
} else {
var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1.
+ if (resolved_name === 0) resolved_name = _malloc(size+1);
for (var i = 0; i < size; i++) {
{{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}};
}
@@ -3605,28 +3529,6 @@ LibraryManager.library = {
return pdest|0;
},
- strlwr__deps:['tolower'],
- strlwr: function(pstr){
- var i = 0;
- while(1) {
- var x = {{{ makeGetValue('pstr', 'i', 'i8') }}};
- if (x == 0) break;
- {{{ makeSetValue('pstr', 'i', '_tolower(x)', 'i8') }}};
- i++;
- }
- },
-
- strupr__deps:['toupper'],
- strupr: function(pstr){
- var i = 0;
- while(1) {
- var x = {{{ makeGetValue('pstr', 'i', 'i8') }}};
- if (x == 0) break;
- {{{ makeSetValue('pstr', 'i', '_toupper(x)', 'i8') }}};
- i++;
- }
- },
-
strcat__asm: true,
strcat__sig: 'iii',
strcat__deps: ['strlen'],
@@ -3667,132 +3569,6 @@ LibraryManager.library = {
// ctype.h
// ==========================================================================
- isascii: function(chr) {
- return chr >= 0 && (chr & 0x80) == 0;
- },
- toascii: function(chr) {
- return chr & 0x7F;
- },
- toupper: function(chr) {
- if (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) {
- return chr - {{{ charCode('a') }}} + {{{ charCode('A') }}};
- } else {
- return chr;
- }
- },
- _toupper: 'toupper',
- toupper_l__deps: ['toupper'],
- toupper_l: function(str, endptr, base) {
- return _toupper(str, endptr, base); // no locale support yet
- },
-
- tolower__asm: true,
- tolower__sig: 'ii',
- tolower: function(chr) {
- chr = chr|0;
- if ((chr|0) < {{{ charCode('A') }}}) return chr|0;
- if ((chr|0) > {{{ charCode('Z') }}}) return chr|0;
- return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0;
- },
- _tolower: 'tolower',
- tolower_l__deps: ['tolower'],
- tolower_l: function(chr) {
- return _tolower(chr); // no locale support yet
- },
-
- // The following functions are defined as macros in glibc.
- islower: function(chr) {
- return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}};
- },
- islower_l__deps: ['islower'],
- islower_l: function(chr) {
- return _islower(chr); // no locale support yet
- },
- isupper: function(chr) {
- return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}};
- },
- isupper_l__deps: ['isupper'],
- isupper_l: function(chr) {
- return _isupper(chr); // no locale support yet
- },
- isalpha: function(chr) {
- return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
- (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
- },
- isalpha_l__deps: ['isalpha'],
- isalpha_l: function(chr) {
- return _isalpha(chr); // no locale support yet
- },
- isdigit: function(chr) {
- return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}};
- },
- isdigit_l__deps: ['isdigit'],
- isdigit_l: function(chr) {
- return _isdigit(chr); // no locale support yet
- },
- isxdigit: function(chr) {
- return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
- (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) ||
- (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}});
- },
- isxdigit_l__deps: ['isxdigit'],
- isxdigit_l: function(chr) {
- return _isxdigit(chr); // no locale support yet
- },
- isalnum: function(chr) {
- return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) ||
- (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) ||
- (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}});
- },
- isalnum_l__deps: ['isalnum'],
- isalnum_l: function(chr) {
- return _isalnum(chr); // no locale support yet
- },
- ispunct: function(chr) {
- return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) ||
- (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) ||
- (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) ||
- (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
- },
- ispunct_l__deps: ['ispunct'],
- ispunct_l: function(chr) {
- return _ispunct(chr); // no locale support yet
- },
- isspace: function(chr) {
- return (chr == 32) || (chr >= 9 && chr <= 13);
- },
- isspace_l__deps: ['isspace'],
- isspace_l: function(chr) {
- return _isspace(chr); // no locale support yet
- },
- isblank: function(chr) {
- return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
- },
- isblank_l__deps: ['isblank'],
- isblank_l: function(chr) {
- return _isblank(chr); // no locale support yet
- },
- iscntrl: function(chr) {
- return (0 <= chr && chr <= 0x1F) || chr === 0x7F;
- },
- iscntrl_l__deps: ['iscntrl'],
- iscntrl_l: function(chr) {
- return _iscntrl(chr); // no locale support yet
- },
- isprint: function(chr) {
- return 0x1F < chr && chr < 0x7F;
- },
- isprint_l__deps: ['isprint'],
- isprint_l: function(chr) {
- return _isprint(chr); // no locale support yet
- },
- isgraph: function(chr) {
- return 0x20 < chr && chr < 0x7F;
- },
- isgraph_l__deps: ['isgraph'],
- isgraph_l: function(chr) {
- return _isgraph(chr); // no locale support yet
- },
// Lookup tables for glibc ctype implementation.
__ctype_b_loc__deps: ['malloc'],
__ctype_b_loc: function() {
@@ -3908,12 +3684,18 @@ LibraryManager.library = {
{{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}};
},
+ llvm_bswap_i16__asm: true,
+ llvm_bswap_i16__sig: 'ii',
llvm_bswap_i16: function(x) {
- return ((x&0xff)<<8) | ((x>>8)&0xff);
+ x = x|0;
+ return (((x&0xff)<<8) | ((x>>8)&0xff))|0;
},
+ llvm_bswap_i32__asm: true,
+ llvm_bswap_i32__sig: 'ii',
llvm_bswap_i32: function(x) {
- return ((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24);
+ x = x|0;
+ return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0;
},
llvm_bswap_i64__deps: ['llvm_bswap_i32'],
@@ -4670,23 +4452,6 @@ LibraryManager.library = {
{{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}};
return x - {{{ makeGetValue('intpart', 0, 'float') }}};
},
- frexp: function(x, exp_addr) {
- var sig = 0, exp_ = 0;
- if (x !== 0) {
- var sign = 1;
- if (x < 0) {
- x = -x;
- sign = -1;
- }
- var raw_exp = Math.log(x)/Math.log(2);
- exp_ = Math.ceil(raw_exp);
- if (exp_ === raw_exp) exp_ += 1;
- sig = sign*x/Math.pow(2, exp_);
- }
- {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}};
- return sig;
- },
- frexpf: 'frexp',
finite: function(x) {
return isFinite(x);
},
@@ -5771,7 +5536,7 @@ LibraryManager.library = {
pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), '');
}
- var matches = new RegExp('^'+pattern).exec(Pointer_stringify(buf))
+ var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf))
// Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches));
function initDate() {
@@ -6084,15 +5849,15 @@ LibraryManager.library = {
var i = 0;
setjmpId = (setjmpId+1)|0;
{{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}};
- while ((i|0) < {{{ 2*MAX_SETJMPS }}}) {
- if ({{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}} == 0) {
- {{{ makeSetValueAsm('table', '(i<<2)', 'setjmpId', 'i32') }}};
- {{{ makeSetValueAsm('table', '(i<<2)+4', 'label', 'i32') }}};
+ while ((i|0) < {{{ MAX_SETJMPS }}}) {
+ if ({{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}} == 0) {
+ {{{ makeSetValueAsm('table', '(i<<3)', 'setjmpId', 'i32') }}};
+ {{{ makeSetValueAsm('table', '(i<<3)+4', 'label', 'i32') }}};
// prepare next slot
- {{{ makeSetValueAsm('table', '(i<<2)+8', '0', 'i32') }}};
+ {{{ makeSetValueAsm('table', '(i<<3)+8', '0', 'i32') }}};
return 0;
}
- i = (i+2)|0;
+ i = i+1|0;
}
{{{ makePrintChars('too many setjmps in a function call, build with a higher value for MAX_SETJMPS') }}};
abort(0);
@@ -6106,12 +5871,12 @@ LibraryManager.library = {
table = table|0;
var i = 0, curr = 0;
while ((i|0) < {{{ MAX_SETJMPS }}}) {
- curr = {{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}};
+ curr = {{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}};
if ((curr|0) == 0) break;
if ((curr|0) == (id|0)) {
- return {{{ makeGetValueAsm('table', '(i<<2)+4', 'i32') }}};
+ return {{{ makeGetValueAsm('table', '(i<<3)+4', 'i32') }}};
}
- i = (i+2)|0;
+ i = i+1|0;
}
return 0;
},
@@ -6197,8 +5962,10 @@ LibraryManager.library = {
raise__deps: ['$ERRNO_CODES', '__setErrNo'],
raise: function(sig) {
- // TODO:
___setErrNo(ERRNO_CODES.ENOSYS);
+#if ASSERTIONS
+ Runtime.warnOnce('raise() returning an error as we do not support it');
+#endif
return -1;
},
diff --git a/src/library_browser.js b/src/library_browser.js
index 4be7315e..57ca5a24 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -196,41 +196,42 @@ mergeInto(LibraryManager.library, {
// Canvas event setup
var canvas = Module['canvas'];
-
- // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module
- // Module['forcedAspectRatio'] = 4 / 3;
-
- canvas.requestPointerLock = canvas['requestPointerLock'] ||
- canvas['mozRequestPointerLock'] ||
- canvas['webkitRequestPointerLock'] ||
- canvas['msRequestPointerLock'] ||
- function(){};
- canvas.exitPointerLock = document['exitPointerLock'] ||
- document['mozExitPointerLock'] ||
- document['webkitExitPointerLock'] ||
- document['msExitPointerLock'] ||
- function(){}; // no-op if function does not exist
- canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
-
- function pointerLockChange() {
- Browser.pointerLock = document['pointerLockElement'] === canvas ||
- document['mozPointerLockElement'] === canvas ||
- document['webkitPointerLockElement'] === canvas ||
- document['msPointerLockElement'] === canvas;
- }
+ if (canvas) {
+ // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module
+ // Module['forcedAspectRatio'] = 4 / 3;
+
+ canvas.requestPointerLock = canvas['requestPointerLock'] ||
+ canvas['mozRequestPointerLock'] ||
+ canvas['webkitRequestPointerLock'] ||
+ canvas['msRequestPointerLock'] ||
+ function(){};
+ canvas.exitPointerLock = document['exitPointerLock'] ||
+ document['mozExitPointerLock'] ||
+ document['webkitExitPointerLock'] ||
+ document['msExitPointerLock'] ||
+ function(){}; // no-op if function does not exist
+ canvas.exitPointerLock = canvas.exitPointerLock.bind(document);
+
+ function pointerLockChange() {
+ Browser.pointerLock = document['pointerLockElement'] === canvas ||
+ document['mozPointerLockElement'] === canvas ||
+ document['webkitPointerLockElement'] === canvas ||
+ document['msPointerLockElement'] === canvas;
+ }
- document.addEventListener('pointerlockchange', pointerLockChange, false);
- document.addEventListener('mozpointerlockchange', pointerLockChange, false);
- document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
- document.addEventListener('mspointerlockchange', pointerLockChange, false);
+ document.addEventListener('pointerlockchange', pointerLockChange, false);
+ document.addEventListener('mozpointerlockchange', pointerLockChange, false);
+ document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
+ document.addEventListener('mspointerlockchange', pointerLockChange, false);
- if (Module['elementPointerLock']) {
- canvas.addEventListener("click", function(ev) {
- if (!Browser.pointerLock && canvas.requestPointerLock) {
- canvas.requestPointerLock();
- ev.preventDefault();
- }
- }, false);
+ if (Module['elementPointerLock']) {
+ canvas.addEventListener("click", function(ev) {
+ if (!Browser.pointerLock && canvas.requestPointerLock) {
+ canvas.requestPointerLock();
+ ev.preventDefault();
+ }
+ }, false);
+ }
}
},
@@ -321,11 +322,6 @@ mergeInto(LibraryManager.library, {
#endif
// Set the background of the WebGL canvas to black
canvas.style.backgroundColor = "black";
-
- // Warn on context loss
- canvas.addEventListener('webglcontextlost', function(event) {
- alert('WebGL context lost. You will need to reload the page.');
- }, false);
}
if (setInModule) {
GLctx = Module.ctx = ctx;
@@ -429,11 +425,13 @@ mergeInto(LibraryManager.library, {
});
},
safeSetTimeout: function(func, timeout) {
+ Module['noExitRuntime'] = true;
return setTimeout(function() {
if (!ABORT) func();
}, timeout);
},
safeSetInterval: function(func, timeout) {
+ Module['noExitRuntime'] = true;
return setInterval(function() {
if (!ABORT) func();
}, timeout);
@@ -475,7 +473,21 @@ mergeInto(LibraryManager.library, {
},
getMouseWheelDelta: function(event) {
- return Math.max(-1, Math.min(1, event.type === 'DOMMouseScroll' ? event.detail : -event.wheelDelta));
+ var delta = 0;
+ switch (event.type) {
+ case 'DOMMouseScroll':
+ delta = event.detail;
+ break;
+ case 'mousewheel':
+ delta = -event.wheelDelta;
+ break;
+ case 'wheel':
+ delta = event.deltaY;
+ break;
+ default:
+ throw 'unrecognized mouse wheel event: ' + event.type;
+ }
+ return Math.max(-1, Math.min(1, delta));
},
mouseX: 0,
@@ -685,15 +697,22 @@ mergeInto(LibraryManager.library, {
emscripten_async_wget: function(url, file, onload, onerror) {
var _url = Pointer_stringify(url);
var _file = Pointer_stringify(file);
+ function doCallback(callback) {
+ if (callback) {
+ var stack = Runtime.stackSave();
+ Runtime.dynCall('vi', callback, [allocate(intArrayFromString(_file), 'i8', ALLOC_STACK)]);
+ Runtime.stackRestore(stack);
+ }
+ }
FS.createPreloadedFile(
PATH.dirname(_file),
PATH.basename(_file),
_url, true, true,
function() {
- if (onload) Runtime.dynCall('vi', onload, [file]);
+ doCallback(onload);
},
function() {
- if (onerror) Runtime.dynCall('vi', onerror, [file]);
+ doCallback(onerror);
}
);
},
@@ -724,7 +743,11 @@ mergeInto(LibraryManager.library, {
http.onload = function http_onload(e) {
if (http.status == 200) {
FS.createDataFile( _file.substr(0, index), _file.substr(index + 1), new Uint8Array(http.response), true, true);
- if (onload) Runtime.dynCall('vii', onload, [arg, file]);
+ if (onload) {
+ var stack = Runtime.stackSave();
+ Runtime.dynCall('vii', onload, [arg, allocate(intArrayFromString(_file), 'i8', ALLOC_STACK)]);
+ Runtime.stackRestore(stack);
+ }
} else {
if (onerror) Runtime.dynCall('vii', onerror, [arg, http.status]);
}
@@ -737,8 +760,8 @@ mergeInto(LibraryManager.library, {
// PROGRESS
http.onprogress = function http_onprogress(e) {
- if (e.lengthComputable || (e.lengthComputable === undefined && e.totalSize != 0)) {
- var percentComplete = (e.position / e.totalSize)*100;
+ if (e.lengthComputable || (e.lengthComputable === undefined && e.total != 0)) {
+ var percentComplete = (e.loaded / e.total)*100;
if (onprogress) Runtime.dynCall('vii', onprogress, [arg, percentComplete]);
}
};
@@ -886,6 +909,8 @@ mergeInto(LibraryManager.library, {
emscripten_set_main_loop: function(func, fps, simulateInfiniteLoop, arg) {
Module['noExitRuntime'] = true;
+ assert(!Browser.mainLoop.scheduler, 'there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one, if you want to');
+
Browser.mainLoop.runner = function Browser_mainLoop_runner() {
if (ABORT) return;
if (Browser.mainLoop.queue.length > 0) {
@@ -1150,6 +1175,39 @@ mergeInto(LibraryManager.library, {
var info = Browser.workers[id];
if (!info) return -1;
return info.awaited;
+ },
+
+ emscripten_get_preloaded_image_data: function(path, w, h) {
+ if (typeof path === "number") {
+ path = Pointer_stringify(path);
+ }
+
+ path = PATH.resolve(path);
+
+ var canvas = Module["preloadedImages"][path];
+ if (canvas) {
+ var ctx = canvas.getContext("2d");
+ var image = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ var buf = _malloc(canvas.width * canvas.height * 4);
+
+ HEAPU8.set(image.data, buf);
+
+ {{{ makeSetValue('w', '0', 'canvas.width', 'i32') }}};
+ {{{ makeSetValue('h', '0', 'canvas.height', 'i32') }}};
+ return buf;
+ }
+
+ return 0;
+ },
+
+ emscripten_get_preloaded_image_data_from_FILE__deps: ['emscripten_get_preloaded_image_data'],
+ emscripten_get_preloaded_image_data_from_FILE: function(file, w, h) {
+ var stream = FS.getStreamFromPtr(file);
+ if (stream) {
+ return _emscripten_get_preloaded_image_data(stream.path, w, h);
+ }
+
+ return 0;
}
});
diff --git a/src/library_fs.js b/src/library_fs.js
index 3d0036ee..5f7f1dea 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -26,7 +26,13 @@ mergeInto(LibraryManager.library, {
// This is set to false when the runtime is initialized, allowing you
// to modify the filesystem freely before run() is called.
ignorePermissions: true,
-
+ trackingDelegate: {},
+ tracking: {
+ openFlags: {
+ READ: 1 << 0,
+ WRITE: 1 << 1
+ }
+ },
ErrnoError: null, // set during init
genericErrors: {},
@@ -394,16 +400,12 @@ mergeInto(LibraryManager.library, {
}
});
}
- if (stream.__proto__) {
- // reuse the object
- stream.__proto__ = FS.FSStream.prototype;
- } else {
- var newStream = new FS.FSStream();
- for (var p in stream) {
- newStream[p] = stream[p];
- }
- stream = newStream;
+ // clone it, so we can return an instance of FSStream
+ var newStream = new FS.FSStream();
+ for (var p in stream) {
+ newStream[p] = stream[p];
}
+ stream = newStream;
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
FS.streams[fd] = stream;
@@ -717,6 +719,13 @@ mergeInto(LibraryManager.library, {
throw new FS.ErrnoError(err);
}
}
+ try {
+ if (FS.trackingDelegate['willMovePath']) {
+ FS.trackingDelegate['willMovePath'](old_path, new_path);
+ }
+ } catch(e) {
+ console.log("FS.trackingDelegate['willMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message);
+ }
// remove the node from the lookup hash
FS.hashRemoveNode(old_node);
// do the underlying fs rename
@@ -729,6 +738,11 @@ mergeInto(LibraryManager.library, {
// changed its name)
FS.hashAddNode(old_node);
}
+ try {
+ if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path);
+ } catch(e) {
+ console.log("FS.trackingDelegate['onMovePath']('"+old_path+"', '"+new_path+"') threw an exception: " + e.message);
+ }
},
rmdir: function(path) {
var lookup = FS.lookupPath(path, { parent: true });
@@ -745,8 +759,20 @@ mergeInto(LibraryManager.library, {
if (FS.isMountpoint(node)) {
throw new FS.ErrnoError(ERRNO_CODES.EBUSY);
}
+ try {
+ if (FS.trackingDelegate['willDeletePath']) {
+ FS.trackingDelegate['willDeletePath'](path);
+ }
+ } catch(e) {
+ console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message);
+ }
parent.node_ops.rmdir(parent, name);
FS.destroyNode(node);
+ try {
+ if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);
+ } catch(e) {
+ console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message);
+ }
},
readdir: function(path) {
var lookup = FS.lookupPath(path, { follow: true });
@@ -773,8 +799,20 @@ mergeInto(LibraryManager.library, {
if (FS.isMountpoint(node)) {
throw new FS.ErrnoError(ERRNO_CODES.EBUSY);
}
+ try {
+ if (FS.trackingDelegate['willDeletePath']) {
+ FS.trackingDelegate['willDeletePath'](path);
+ }
+ } catch(e) {
+ console.log("FS.trackingDelegate['willDeletePath']('"+path+"') threw an exception: " + e.message);
+ }
parent.node_ops.unlink(parent, name);
FS.destroyNode(node);
+ try {
+ if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);
+ } catch(e) {
+ console.log("FS.trackingDelegate['onDeletePath']('"+path+"') threw an exception: " + e.message);
+ }
},
readlink: function(path) {
var lookup = FS.lookupPath(path);
@@ -969,6 +1007,20 @@ mergeInto(LibraryManager.library, {
Module['printErr']('read file: ' + path);
}
}
+ try {
+ if (FS.trackingDelegate['onOpenFile']) {
+ var trackingFlags = 0;
+ if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}) {
+ trackingFlags |= FS.tracking.openFlags.READ;
+ }
+ if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}) {
+ trackingFlags |= FS.tracking.openFlags.WRITE;
+ }
+ FS.trackingDelegate['onOpenFile'](path, trackingFlags);
+ }
+ } catch(e) {
+ console.log("FS.trackingDelegate['onOpenFile']('"+path+"', flags) threw an exception: " + e.message);
+ }
return stream;
},
close: function(stream) {
@@ -1038,6 +1090,11 @@ mergeInto(LibraryManager.library, {
}
var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);
if (!seeking) stream.position += bytesWritten;
+ try {
+ if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path);
+ } catch(e) {
+ console.log("FS.trackingDelegate['onWriteToFile']('"+path+"') threw an exception: " + e.message);
+ }
return bytesWritten;
},
allocate: function(stream, offset, length) {
diff --git a/src/library_gl.js b/src/library_gl.js
index 851b01b1..2659a9d9 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -431,21 +431,42 @@ var LibraryGL = {
sizePerPixel = 2;
break;
default:
- throw 'Invalid format (' + format + ')';
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format);
+#endif
+ return {
+ pixels: null,
+ internalFormat: 0x0
+ };
}
break;
case 0x1403 /* GL_UNSIGNED_SHORT */:
if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
sizePerPixel = 2;
} else {
- throw 'Invalid format (' + format + ')';
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format);
+#endif
+ return {
+ pixels: null,
+ internalFormat: 0x0
+ };
}
break;
case 0x1405 /* GL_UNSIGNED_INT */:
if (format == 0x1902 /* GL_DEPTH_COMPONENT */) {
sizePerPixel = 4;
} else {
- throw 'Invalid format (' + format + ')';
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format);
+#endif
+ return {
+ pixels: null,
+ internalFormat: 0x0
+ };
}
break;
case 0x84FA /* UNSIGNED_INT_24_8_WEBGL */:
@@ -468,12 +489,26 @@ var LibraryGL = {
sizePerPixel = 4*4;
break;
default:
- throw 'Invalid format (' + format + ')';
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format);
+#endif
+ return {
+ pixels: null,
+ internalFormat: 0x0
+ };
}
internalFormat = GLctx.RGBA;
break;
default:
- throw 'Invalid type (' + type + ')';
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glTex[Sub]Image, type: ' + type + ', format: ' + format);
+#endif
+ return {
+ pixels: null,
+ internalFormat: 0x0
+ };
}
var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment);
if (type == 0x1401 /* GL_UNSIGNED_BYTE */) {
@@ -488,7 +523,7 @@ var LibraryGL = {
return {
pixels: pixels,
internalFormat: internalFormat
- }
+ };
},
#if GL_FFP_ONLY
diff --git a/src/library_glfw.js b/src/library_glfw.js
index 04dd4a0a..6dfea101 100644
--- a/src/library_glfw.js
+++ b/src/library_glfw.js
@@ -211,7 +211,7 @@ var LibraryGLFW = {
},
onMouseWheel: function(event) {
- GLFW.wheelPos += Browser.getMouseWheelDelta(event);
+ GLFW.wheelPos -= Browser.getMouseWheelDelta(event);
if (GLFW.mouseWheelFunc && event.target == Module["canvas"]) {
Runtime.dynCall('vi', GLFW.mouseWheelFunc, [GLFW.wheelPos]);
diff --git a/src/library_html5.js b/src/library_html5.js
index d9376c2a..d5d0cd66 100644
--- a/src/library_html5.js
+++ b/src/library_html5.js
@@ -1307,6 +1307,12 @@ var LibraryJSEvents = {
JSEvents.registerWebGlEventCallback(target, userData, useCapture, callbackfunc, {{{ cDefine('EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED') }}}, "webglcontextrestored");
return {{{ cDefine('EMSCRIPTEN_RESULT_SUCCESS') }}};
},
+
+ emscripten_is_webgl_context_lost: function(target) {
+ // TODO: In the future if multiple GL contexts are supported, use the 'target' parameter to find the canvas to query.
+ if (!Module['ctx']) return true; // No context ~> lost context.
+ return Module['ctx'].isContextLost();
+ }
};
autoAddDeps(LibraryJSEvents, '$JSEvents');
diff --git a/src/library_sdl.js b/src/library_sdl.js
index eedb8c48..a01b3c6c 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -430,6 +430,15 @@ var LibrarySDL = {
savedKeydown: null,
receiveEvent: function(event) {
+ function unpressAllPressedKeys() {
+ // Un-press all pressed keys: TODO
+ for (var code in SDL.keyboardMap) {
+ SDL.events.push({
+ type: 'keyup',
+ keyCode: SDL.keyboardMap[code]
+ });
+ }
+ };
switch(event.type) {
case 'touchstart': case 'touchmove': {
event.preventDefault();
@@ -532,7 +541,7 @@ var LibrarySDL = {
}
}
// fall through
- case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel':
+ case 'keydown': case 'keyup': case 'keypress': case 'mousedown': case 'mouseup': case 'DOMMouseScroll': case 'mousewheel': case 'wheel':
// If we preventDefault on keydown events, the subsequent keypress events
// won't fire. However, it's fine (and in some cases necessary) to
// preventDefault for keys that don't generate a character. Otherwise,
@@ -541,21 +550,40 @@ var LibrarySDL = {
event.preventDefault();
}
- if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel') {
+ if (event.type == 'DOMMouseScroll' || event.type == 'mousewheel' || event.type == 'wheel') {
+ // Simulate old-style SDL events representing mouse wheel input as buttons
var button = Browser.getMouseWheelDelta(event) > 0 ? 4 : 3;
- var event2 = {
+ var event1 = {
type: 'mousedown',
button: button,
pageX: event.pageX,
pageY: event.pageY
};
- SDL.events.push(event2);
- event = {
+ SDL.events.push(event1);
+ var event2 = {
type: 'mouseup',
button: button,
pageX: event.pageX,
pageY: event.pageY
};
+ SDL.events.push(event2);
+
+ // Convert DOMMouseScroll events to wheel events for new style SDL events.
+ if (event.type == 'DOMMouseScroll') {
+ SDL.events.push({
+ type: 'wheel',
+ deltaX: 0,
+ deltaY: -event.detail,
+ });
+ break;
+ } else if (event.type == 'mousewheel') {
+ SDL.events.push({
+ type: 'wheel',
+ deltaX: 0,
+ deltaY: event.wheelDelta,
+ });
+ break;
+ }
} else if (event.type == 'mousedown') {
SDL.DOMButtons[event.button] = 1;
SDL.events.push({
@@ -635,18 +663,23 @@ var LibrarySDL = {
}
event.preventDefault();
break;
+ case 'focus':
+ SDL.events.push(event);
+ event.preventDefault();
+ break;
case 'blur':
- case 'visibilitychange': {
- // Un-press all pressed keys: TODO
- for (var code in SDL.keyboardMap) {
- SDL.events.push({
- type: 'keyup',
- keyCode: SDL.keyboardMap[code]
- });
- }
+ SDL.events.push(event);
+ unpressAllPressedKeys();
+ event.preventDefault();
+ break;
+ case 'visibilitychange':
+ SDL.events.push({
+ type: 'visibilitychange',
+ visible: !document.hidden
+ });
+ unpressAllPressedKeys();
event.preventDefault();
break;
- }
case 'unload':
if (Browser.mainLoop.runner) {
SDL.events.push(event);
@@ -686,7 +719,6 @@ var LibrarySDL = {
} else {
code = SDL.keyCodes[event.keyCode] || event.keyCode;
}
-
{{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
// TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED
SDL.modState = ({{{ makeGetValue('SDL.keyboardState', '1248', 'i8') }}} ? 0x0040 | 0x0080 : 0) | // KMOD_LCTRL & KMOD_RCTRL
@@ -788,8 +820,15 @@ var LibrarySDL = {
}
break;
}
+ case 'wheel': {
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.x, 'event.deltaX', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_MouseWheelEvent.y, 'event.deltaY', 'i32') }}};
+ break;
+ }
case 'touchstart': case 'touchend': case 'touchmove': {
var touch = event.touch;
+ if (!Browser.touches[touch.identifier]) break;
var w = Module['canvas'].width;
var h = Module['canvas'].height;
var x = Browser.touches[touch.identifier].x / w;
@@ -840,6 +879,29 @@ var LibrarySDL = {
{{{ makeSetValue('ptr', C_STRUCTS.SDL_JoyAxisEvent.value, 'SDL.joystickAxisValueConversion(event.value)', 'i32') }}};
break;
}
+ case 'focus': {
+ var SDL_WINDOWEVENT_FOCUS_GAINED = 12 /* SDL_WINDOWEVENT_FOCUS_GAINED */;
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, 'SDL_WINDOWEVENT_FOCUS_GAINED', 'i8') }}};
+ break;
+ }
+ case 'blur': {
+ var SDL_WINDOWEVENT_FOCUS_LOST = 13 /* SDL_WINDOWEVENT_FOCUS_LOST */;
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, '0', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, 'SDL_WINDOWEVENT_FOCUS_LOST', 'i8') }}};
+ break;
+ }
+ case 'visibilitychange': {
+ var SDL_WINDOWEVENT_SHOWN = 1 /* SDL_WINDOWEVENT_SHOWN */;
+ var SDL_WINDOWEVENT_HIDDEN = 2 /* SDL_WINDOWEVENT_HIDDEN */;
+ var visibilityEventID = event.visible ? SDL_WINDOWEVENT_SHOWN : SDL_WINDOWEVENT_HIDDEN;
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.type, 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.windowID, 0, 'i32') }}};
+ {{{ makeSetValue('ptr', C_STRUCTS.SDL_WindowEvent.event, 'visibilityEventID' , 'i8') }}};
+ break;
+ }
default: throw 'Unhandled SDL event: ' + event.type;
}
},
@@ -880,12 +942,80 @@ var LibrarySDL = {
if (!info) return 0;
var ret = info.volume * 128; // MIX_MAX_VOLUME
if (volume != -1) {
- info.volume = volume / 128;
- if (info.audio) info.audio.volume = info.volume;
+ info.volume = Math.min(Math.max(volume, 0), 128) / 128;
+ if (info.audio) {
+ try {
+ info.audio.volume = info.volume; // For <audio> element
+ if (info.audio.webAudioGainNode) info.audio.webAudioGainNode['gain']['value'] = info.volume; // For WebAudio playback
+ } catch(e) {
+ Module.printErr('setGetVolume failed to set audio volume: ' + e);
+ }
+ }
}
return ret;
},
+ // Plays out an SDL audio resource that was loaded with the Mix_Load APIs, when using Web Audio..
+ playWebAudio: function(audio) {
+ if (!audio) return;
+ if (audio.webAudioNode) return; // This instance is already playing, don't start again.
+ if (!SDL.webAudioAvailable()) return;
+ try {
+ var webAudio = audio.resource.webAudio;
+ audio.paused = false;
+ if (!webAudio.decodedBuffer) {
+ if (webAudio.onDecodeComplete === undefined) abort("Cannot play back audio object that was not loaded");
+ webAudio.onDecodeComplete.push(function() { if (!audio.paused) SDL.playWebAudio(audio); });
+ return;
+ }
+ audio.webAudioNode = SDL.audioContext['createBufferSource']();
+ audio.webAudioNode['buffer'] = webAudio.decodedBuffer;
+ audio.webAudioNode['loop'] = audio.loop;
+ audio.webAudioNode['onended'] = function() { audio.onended(); } // For <media> element compatibility, route the onended signal to the instance.
+
+ // Add an intermediate gain node to control volume.
+ audio.webAudioGainNode = SDL.audioContext['createGain']();
+ audio.webAudioGainNode['gain']['value'] = audio.volume;
+ audio.webAudioNode['connect'](audio.webAudioGainNode);
+ audio.webAudioGainNode['connect'](SDL.audioContext['destination']);
+ audio.webAudioNode['start'](0, audio.currentPosition);
+ audio.startTime = SDL.audioContext['currentTime'] - audio.currentPosition;
+ } catch(e) {
+ Module.printErr('playWebAudio failed: ' + e);
+ }
+ },
+
+ // Pausea an SDL audio resource that was played with Web Audio..
+ pauseWebAudio: function(audio) {
+ if (!audio) return;
+ if (audio.webAudioNode) {
+ try {
+ // Remember where we left off, so that if/when we resume, we can restart the playback at a proper place.
+ audio.currentPosition = (SDL.audioContext['currentTime'] - audio.startTime) % audio.resource.webAudio.decodedBuffer.duration;
+ // Important: When we reach here, the audio playback is stopped by the user. But when calling .stop() below, the Web Audio
+ // graph will send the onended signal, but we don't want to process that, since pausing should not clear/destroy the audio
+ // channel.
+ audio.webAudioNode['onended'] = undefined;
+ audio.webAudioNode.stop();
+ audio.webAudioNode = undefined;
+ } catch(e) {
+ Module.printErr('pauseWebAudio failed: ' + e);
+ }
+ }
+ audio.paused = true;
+ },
+
+ openAudioContext: function() {
+ // Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
+ // since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'.
+ if (!SDL.audioContext) {
+ if (typeof(AudioContext) !== 'undefined') SDL.audioContext = new AudioContext();
+ else if (typeof(webkitAudioContext) !== 'undefined') SDL.audioContext = new webkitAudioContext();
+ }
+ },
+
+ webAudioAvailable: function() { return !!SDL.audioContext; },
+
fillWebAudioBufferFromHeap: function(heapPtr, sizeSamplesPerChannel, dstAudioBuffer) {
// The input audio data is interleaved across the channels, i.e. [L, R, L, R, L, R, ...] and is either 8-bit or 16-bit as
// supported by the SDL API. The output audio wave data for Web Audio API must be in planar buffers of [-1,1]-normalized Float32 data,
@@ -1043,6 +1173,7 @@ var LibrarySDL = {
document.addEventListener("keydown", SDL.receiveEvent);
document.addEventListener("keyup", SDL.receiveEvent);
document.addEventListener("keypress", SDL.receiveEvent);
+ window.addEventListener("focus", SDL.receiveEvent);
window.addEventListener("blur", SDL.receiveEvent);
document.addEventListener("visibilitychange", SDL.receiveEvent);
}
@@ -1065,11 +1196,16 @@ var LibrarySDL = {
SDL.DOMEventToSDLEvent['mousedown'] = 0x401 /* SDL_MOUSEBUTTONDOWN */;
SDL.DOMEventToSDLEvent['mouseup'] = 0x402 /* SDL_MOUSEBUTTONUP */;
SDL.DOMEventToSDLEvent['mousemove'] = 0x400 /* SDL_MOUSEMOTION */;
+ SDL.DOMEventToSDLEvent['wheel'] = 0x403 /* SDL_MOUSEWHEEL */;
SDL.DOMEventToSDLEvent['touchstart'] = 0x700 /* SDL_FINGERDOWN */;
SDL.DOMEventToSDLEvent['touchend'] = 0x701 /* SDL_FINGERUP */;
SDL.DOMEventToSDLEvent['touchmove'] = 0x702 /* SDL_FINGERMOTION */;
SDL.DOMEventToSDLEvent['unload'] = 0x100 /* SDL_QUIT */;
SDL.DOMEventToSDLEvent['resize'] = 0x7001 /* SDL_VIDEORESIZE/SDL_EVENT_COMPAT2 */;
+ SDL.DOMEventToSDLEvent['visibilitychange'] = 0x200 /* SDL_WINDOWEVENT */;
+ SDL.DOMEventToSDLEvent['focus'] = 0x200 /* SDL_WINDOWEVENT */;
+ SDL.DOMEventToSDLEvent['blur'] = 0x200 /* SDL_WINDOWEVENT */;
+
// These are not technically DOM events; the HTML gamepad API is poll-based.
// However, we define them here, as the rest of the SDL code assumes that
// all SDL events originate as DOM events.
@@ -1139,7 +1275,7 @@ var LibrarySDL = {
},
SDL_SetVideoMode: function(width, height, depth, flags) {
- ['touchstart', 'touchend', 'touchmove', 'mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) {
+ ['touchstart', 'touchend', 'touchmove', 'mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'wheel', 'mouseout'].forEach(function(event) {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
@@ -1182,11 +1318,11 @@ var LibrarySDL = {
for (var i = 0; i < SDL.numChannels; ++i) {
if (SDL.channels[i].audio) {
SDL.channels[i].audio.pause();
+ SDL.channels[i].audio = undefined;
}
}
- if (SDL.music.audio) {
- SDL.music.audio.pause();
- }
+ if (SDL.music.audio) SDL.music.audio.pause();
+ SDL.music.audio = undefined;
Module.print('SDL_Quit called (and ignored)');
},
@@ -1936,15 +2072,8 @@ var LibrarySDL = {
} else {
// Initialize Web Audio API if we haven't done so yet. Note: Only initialize Web Audio context ever once on the web page,
// since initializing multiple times fails on Chrome saying 'audio resources have been exhausted'.
- if (!SDL.audioContext) {
- if (typeof(AudioContext) !== 'undefined') {
- SDL.audioContext = new AudioContext();
- } else if (typeof(webkitAudioContext) !== 'undefined') {
- SDL.audioContext = new webkitAudioContext();
- } else {
- throw 'Web Audio API is not available!';
- }
- }
+ SDL.openAudioContext();
+ if (!SDL.audioContext) throw 'Web Audio API is not available!';
SDL.audio.soundSource = new Array(); // Use an array of sound sources as a ring buffer to queue blocks of synthesized audio to Web Audio API.
SDL.audio.nextSoundSource = 0; // Index of the next sound buffer in the ring buffer queue to play.
SDL.audio.nextPlayTime = 0; // Time in seconds when the next audio block is due to start.
@@ -2127,6 +2256,7 @@ var LibrarySDL = {
Mix_Quit: function(){},
Mix_OpenAudio: function(frequency, format, channels, chunksize) {
+ SDL.openAudioContext();
SDL.allocateChannels(32);
// Just record the values for a later call to Mix_QuickLoad_RAW
SDL.mixerFrequency = frequency;
@@ -2169,6 +2299,7 @@ var LibrarySDL = {
var filename = '';
var audio;
+ var webAudio;
var bytes;
if (rwops.filename !== undefined) {
@@ -2176,7 +2307,7 @@ var LibrarySDL = {
var raw = Module["preloadedAudios"][filename];
if (!raw) {
if (raw === null) Module.printErr('Trying to reuse preloaded audio, but freePreloadedMediaOnUse is set!');
- Runtime.warnOnce('Cannot find preloaded audio ' + filename);
+ if (!Module.noAudioDecoding) Runtime.warnOnce('Cannot find preloaded audio ' + filename);
// see if we can read the file-contents from the in-memory FS
try {
@@ -2192,45 +2323,84 @@ var LibrarySDL = {
audio = raw;
}
else if (rwops.bytes !== undefined) {
- bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count);
+ // For Web Audio context buffer decoding, we must make a clone of the audio data, but for <media> element,
+ // a view to existing data is sufficient.
+ if (SDL.webAudioAvailable()) bytes = HEAPU8.buffer.slice(rwops.bytes, rwops.bytes + rwops.count);
+ else bytes = HEAPU8.subarray(rwops.bytes, rwops.bytes + rwops.count);
}
else {
return 0;
}
- // Here, we didn't find a preloaded audio but we either were passed a filepath for
- // which we loaded bytes, or we were passed some bytes
- if (audio === undefined && bytes) {
+ var arrayBuffer = bytes ? bytes.buffer || bytes : bytes;
+
+ // To allow user code to work around browser bugs with audio playback on <audio> elements an Web Audio, enable
+ // the user code to hook in a callback to decide on a file basis whether each file should use Web Audio or <audio> for decoding and playback.
+ // In particular, see https://bugzilla.mozilla.org/show_bug.cgi?id=654787 and ?id=1012801 for tradeoffs.
+ var canPlayWithWebAudio = Module['SDL_canPlayWithWebAudio'] === undefined || Module['SDL_canPlayWithWebAudio'](filename, arrayBuffer);
+
+ if (bytes !== undefined && SDL.webAudioAvailable() && canPlayWithWebAudio) {
+ audio = undefined;
+ webAudio = {};
+ // The audio decoding process is asynchronous, which gives trouble if user code plays the audio data back immediately
+ // after loading. Therefore prepare an array of callback handlers to run when this audio decoding is complete, which
+ // will then start the playback (with some delay).
+ webAudio.onDecodeComplete = []; // While this member array exists, decoding hasn't finished yet.
+ function onDecodeComplete(data) {
+ webAudio.decodedBuffer = data;
+ // Call all handlers that were waiting for this decode to finish, and clear the handler list.
+ webAudio.onDecodeComplete.forEach(function(e) { e(); });
+ webAudio.onDecodeComplete = undefined; // Don't allow more callback handlers since audio has finished decoding.
+ }
+
+ SDL.audioContext['decodeAudioData'](arrayBuffer, onDecodeComplete);
+ } else if (audio === undefined && bytes) {
+ // Here, we didn't find a preloaded audio but we either were passed a filepath for
+ // which we loaded bytes, or we were passed some bytes
var blob = new Blob([bytes], {type: rwops.mimetype});
var url = URL.createObjectURL(blob);
audio = new Audio();
audio.src = url;
+ audio.mozAudioChannelType = 'content'; // bugzilla 910340
}
var id = SDL.audios.length;
// Keep the loaded audio in the audio arrays, ready for playback
SDL.audios.push({
source: filename,
- audio: audio
+ audio: audio, // Points to the <audio> element, if loaded
+ webAudio: webAudio // Points to a Web Audio -specific resource object, if loaded
});
return id;
},
Mix_QuickLoad_RAW: function(mem, len) {
- var audio = new Audio();
- // Record the number of channels and frequency for later usage
- audio.numChannels = SDL.mixerNumChannels;
- audio.frequency = SDL.mixerFrequency;
+ var audio;
+ var webAudio;
+
var numSamples = len >> 1; // len is the length in bytes, and the array contains 16-bit PCM values
var buffer = new Float32Array(numSamples);
for (var i = 0; i < numSamples; ++i) {
buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?)
}
- // FIXME: doesn't make sense to keep the audio element in the buffer
+
+ if (SDL.webAudioAvailable()) {
+ webAudio = {};
+ webAudio.decodedBuffer = buffer;
+ } else {
+ var audio = new Audio();
+ audio.mozAudioChannelType = 'content'; // bugzilla 910340
+ // Record the number of channels and frequency for later usage
+ audio.numChannels = SDL.mixerNumChannels;
+ audio.frequency = SDL.mixerFrequency;
+ // FIXME: doesn't make sense to keep the audio element in the buffer
+ }
+
var id = SDL.audios.length;
SDL.audios.push({
source: '',
audio: audio,
+ webAudio: webAudio,
buffer: buffer
});
return id;
@@ -2243,13 +2413,12 @@ var LibrarySDL = {
SDL.channelMinimumNumber = num;
},
Mix_PlayChannel: function(channel, id, loops) {
- // TODO: handle loops
+ // TODO: handle fixed amount of N loops. Currently loops either 0 or infinite times.
// Get the audio element associated with the ID
var info = SDL.audios[id];
if (!info) return -1;
- var audio = info.audio;
- if (!audio) return -1;
+ if (!info.audio && !info.webAudio) return -1;
// If the user asks us to allocate a channel automatically, get the first
// free one.
@@ -2265,73 +2434,33 @@ var LibrarySDL = {
return -1;
}
}
- // We clone the audio node to utilize the preloaded audio buffer, since
- // the browser has already preloaded the audio file.
var channelInfo = SDL.channels[channel];
- channelInfo.audio = audio = audio.cloneNode(true);
- audio.numChannels = info.audio.numChannels;
- audio.frequency = info.audio.frequency;
- // TODO: handle N loops. Behavior matches Mix_PlayMusic
- audio.loop = loops != 0;
- audio['onended'] = function SDL_audio_onended() { // TODO: cache these
- channelInfo.audio = null;
- if (SDL.channelFinished) {
- Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
- }
- }
- // Either play the element, or load the dynamic data into it
- if (info.buffer) {
- var contextCtor = null;
- if (audio && ('mozSetup' in audio)) { // Audio Data API
- try {
- audio['mozSetup'](audio.numChannels, audio.frequency);
- audio["mozWriteAudio"](info.buffer);
- } catch (e) {
- // Workaround for Firefox bug 783052
- // ignore this exception!
- }
- /*
- } else if (contextCtor = (window.AudioContext || // WebAudio API
- window.webkitAudioContext)) {
- var currentIndex = 0;
- var numChannels = parseInt(audio.numChannels);
- var context = new contextCtor();
- var source = context.createBufferSource();
- source.loop = false;
- source.buffer = context.createBuffer(numChannels, 1, audio.frequency);
- var jsNode = context.createJavaScriptNode(2048, numChannels, numChannels);
- jsNode.onaudioprocess = function jsNode_onaudioprocess(event) {
- var buffers = new Array(numChannels);
- for (var i = 0; i < numChannels; ++i) {
- buffers[i] = event.outputBuffer.getChannelData(i);
- }
- var remaining = info.buffer.length - currentIndex;
- if (remaining > 2048) {
- remaining = 2048;
- }
- for (var i = 0; i < remaining;) {
- for (var j = 0; j < numChannels; ++j) {
- buffers[j][i] = info.buffer[currentIndex + i + j] * audio.volume;
- }
- i += j;
- }
- currentIndex += remaining * numChannels;
- for (var i = remaining; i < 2048;) {
- for (var j = 0; j < numChannels; ++j) {
- buffers[j][i] = 0; // silence
- }
- i += j;
- }
- };
- source.connect(jsNode);
- jsNode.connect(context.destination);
- source.noteOn(0);
- */
- }
+ var audio;
+ if (info.webAudio) {
+ // Create an instance of the WebAudio object.
+ audio = {};
+ audio.resource = info; // This new object is an instance that refers to this existing resource.
+ audio.paused = false;
+ audio.currentPosition = 0;
+ // Make our instance look similar to the instance of a <media> to make api simple.
+ audio.play = function() { SDL.playWebAudio(this); }
+ audio.pause = function() { SDL.pauseWebAudio(this); }
} else {
- audio.play();
+ // We clone the audio node to utilize the preloaded audio buffer, since
+ // the browser has already preloaded the audio file.
+ audio = info.audio.cloneNode(true);
+ audio.numChannels = info.audio.numChannels;
+ audio.frequency = info.audio.frequency;
}
+ audio['onended'] = function SDL_audio_onended() { // TODO: cache these
+ if (channelInfo.audio == this) { channelInfo.audio.paused = true; channelInfo.audio = null; }
+ if (SDL.channelFinished) Runtime.getFuncWrapper(SDL.channelFinished, 'vi')(channel);
+ }
+ channelInfo.audio = audio;
+ // TODO: handle N loops. Behavior matches Mix_PlayMusic
+ audio.loop = loops != 0;
audio.volume = channelInfo.volume;
+ audio.play();
return channel;
},
Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing
@@ -2384,46 +2513,51 @@ var LibrarySDL = {
Mix_PlayMusic__deps: ['Mix_HaltMusic'],
Mix_PlayMusic: function(id, loops) {
- loops = Math.max(loops, 1);
- var audio = SDL.audios[id].audio;
- if (!audio) return 0;
- audio.loop = loops != 0; // TODO: handle N loops for finite N
- if (SDL.audios[id].buffer) {
- audio["mozWriteAudio"](SDL.audios[id].buffer);
- } else {
- audio.play();
- }
- audio.volume = SDL.music.volume;
- audio['onended'] = _Mix_HaltMusic; // will send callback
+ // Pause old music if it exists.
if (SDL.music.audio) {
- if (!SDL.music.audio.paused) {
- Module.printErr('Music is already playing. ' + SDL.music.source);
- }
+ if (!SDL.music.audio.paused) Module.printErr('Music is already playing. ' + SDL.music.source);
SDL.music.audio.pause();
}
+ var info = SDL.audios[id];
+ var audio;
+ if (info.webAudio) { // Play via Web Audio API
+ // Create an instance of the WebAudio object.
+ audio = {};
+ audio.resource = info; // This new webAudio object is an instance that refers to this existing resource.
+ audio.paused = false;
+ audio.currentPosition = 0;
+ audio.play = function() { SDL.playWebAudio(this); }
+ audio.pause = function() { SDL.pauseWebAudio(this); }
+ } else if (info.audio) { // Play via the <audio> element
+ audio = info.audio;
+ }
+ audio['onended'] = function() { if (SDL.music.audio == this) _Mix_HaltMusic(); } // will send callback
+ audio.loop = loops != 0; // TODO: handle N loops for finite N
+ audio.volume = SDL.music.volume;
SDL.music.audio = audio;
+ audio.play();
return 0;
},
Mix_PauseMusic: function() {
var audio = SDL.music.audio;
- if (!audio) return 0;
- audio.pause();
+ if (audio) audio.pause();
return 0;
},
Mix_ResumeMusic: function() {
var audio = SDL.music.audio;
- if (!audio) return 0;
- audio.play();
+ if (audio) audio.play();
return 0;
},
Mix_HaltMusic: function() {
var audio = SDL.music.audio;
- if (!audio) return 0;
- audio.src = audio.src; // rewind
- audio.pause();
+ if (audio) {
+ audio.src = audio.src; // rewind <media> element
+ audio.currentPosition = 0; // rewind Web Audio graph playback.
+ audio.pause();
+ }
SDL.music.audio = null;
if (SDL.hookMusicFinished) {
Runtime.dynCall('v', SDL.hookMusicFinished);
@@ -2500,9 +2634,7 @@ var LibrarySDL = {
return;
}
var info = SDL.channels[channel];
- if (info && info.audio) {
- info.audio.play();
- }
+ if (info && info.audio) info.audio.play();
},
// SDL TTF
diff --git a/src/modules.js b/src/modules.js
index 2d2a75d0..fd5c23cd 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -21,6 +21,7 @@ var LLVM = {
CONVERSIONS: set('inttoptr', 'ptrtoint', 'uitofp', 'sitofp', 'fptosi', 'fptoui', 'fpext', 'fptrunc'),
INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2
MATHOP_IGNORABLES: set('exact', 'nnan', 'ninf', 'nsz', 'arcp', 'fast'),
+ PARAM_IGNORABLES: set('nocapture', 'readonly', 'readnone'),
};
LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden']));
diff --git a/src/parseTools.js b/src/parseTools.js
index 4fb76196..ececf477 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -467,7 +467,7 @@ function parseParamTokens(params) {
// handle 'byval' and 'byval align X'. We store the alignment in 'byVal'
byVal = QUANTUM_SIZE;
segment.splice(1, 1);
- if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ if (segment[1] && (segment[1].text in LLVM.PARAM_IGNORABLES)) {
segment.splice(1, 1);
}
if (segment[1] && segment[1].text === 'align') {
@@ -476,7 +476,7 @@ function parseParamTokens(params) {
segment.splice(1, 2);
}
}
- if (segment[1] && (segment[1].text === 'nocapture' || segment[1].text === 'readonly')) {
+ if (segment[1] && (segment[1].text in LLVM.PARAM_IGNORABLES)) {
segment.splice(1, 1);
}
if (segment.length == 1) {
@@ -1168,15 +1168,10 @@ function getHeapOffset(offset, type, forceAsm) {
var sz = Runtime.getNativeTypeSize(type);
var shifts = Math.log(sz)/Math.LN2;
offset = '(' + offset + ')';
- if (shifts != 0) {
- if (CHECK_HEAP_ALIGN) {
- return '((CHECK_ALIGN_' + sz + '(' + offset + '|0)|0)>>' + shifts + ')';
- } else {
- return '(' + offset + '>>' + shifts + ')';
- }
+ if (CHECK_HEAP_ALIGN && shifts > 0) {
+ return '((CHECK_ALIGN_' + sz + '(' + offset + '|0)|0)>>' + shifts + ')';
} else {
- // we need to guard against overflows here, HEAP[U]8 expects a guaranteed int
- return isJSVar(offset) ? offset : '(' + offset + '|0)';
+ return '(' + offset + '>>' + shifts + ')';
}
}
@@ -2040,7 +2035,7 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) {
} else if (param.intertype == 'mathop') {
return processMathop(param);
} else if (param.intertype === 'vector') {
- return getVectorBaseType(param.type) + '32x4(' + param.idents.join(',') + ')';
+ return 'SIMD.' + getVectorBaseType(param.type) + '32x4(' + param.idents.join(',') + ')';
} else {
throw 'invalid llvm parameter: ' + param.intertype;
}
@@ -2705,7 +2700,7 @@ var simdLane = ['x', 'y', 'z', 'w'];
function ensureVector(ident, base) {
Types.usesSIMD = true;
- return ident == 0 ? base + '32x4.splat(0)' : ident;
+ return ident == 0 ? 'SIMD.' + base + '32x4.splat(0)' : ident;
}
function ensureValidFFIType(type) {
diff --git a/src/preamble.js b/src/preamble.js
index 89ab5026..ae58e7e0 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -41,13 +41,13 @@ var ACCEPTABLE_SAFE_HEAP_ERRORS = 0;
function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) {
//if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + stackTrace()); // Something like this may be useful, in debugging
- assert(dest > 0, 'segmentation fault');
+ if (dest <= 0) abort('segmentation fault ' + (store ? ('storing value ' + storeValue) : 'loading') + ' type ' + type + ' at address ' + dest);
#if USE_TYPED_ARRAYS
// When using typed arrays, reads over the top of TOTAL_MEMORY will fail silently, so we must
// correct that by growing TOTAL_MEMORY as needed. Without typed arrays, memory is a normal
// JS array so it will work (potentially slowly, depending on the engine).
- assert(ignore || dest < Math.max(DYNAMICTOP, STATICTOP));
+ if (!ignore && dest >= Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault ' + (store ? ('storing value ' + storeValue) : 'loading') + ' type ' + type + ' at address ' + dest + '. Heap ends at address ' + Math.max(DYNAMICTOP, STATICTOP));
assert(ignore || DYNAMICTOP <= TOTAL_MEMORY);
#endif
@@ -186,17 +186,17 @@ function SAFE_HEAP_STORE(dest, value, bytes, isFloat) {
#if SAFE_HEAP_LOG
Module.print('SAFE_HEAP store: ' + [dest, value, bytes, isFloat]);
#endif
- assert(dest > 0, 'segmentation fault');
- assert(dest % bytes === 0, 'alignment error');
- assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)');
+ if (dest <= 0) abort('segmentation fault storing ' + bytes + ' bytes to address ' + dest);
+ if (dest % bytes !== 0) abort('alignment error storing to address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes);
+ if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when storing ' + bytes + ' bytes to address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP);
assert(DYNAMICTOP <= TOTAL_MEMORY);
setValue(dest, value, getSafeHeapType(bytes, isFloat), 1);
}
function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) {
- assert(dest > 0, 'segmentation fault');
- assert(dest % bytes === 0, 'alignment error');
- assert(dest < Math.max(DYNAMICTOP, STATICTOP), 'segmentation fault (high)');
+ if (dest <= 0) abort('segmentation fault loading ' + bytes + ' bytes from address ' + dest);
+ if (dest % bytes !== 0) abort('alignment error loading from address ' + dest + ', which was expected to be aligned to a multiple of ' + bytes);
+ if (dest + bytes > Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault, exceeded the top of the available heap when loading ' + bytes + ' bytes from address ' + dest + '. STATICTOP=' + STATICTOP + ', DYNAMICTOP=' + DYNAMICTOP);
assert(DYNAMICTOP <= TOTAL_MEMORY);
var type = getSafeHeapType(bytes, isFloat);
var ret = getValue(dest, type, 1);
@@ -207,6 +207,14 @@ function SAFE_HEAP_LOAD(dest, bytes, isFloat, unsigned) {
return ret;
}
+function SAFE_FT_MASK(value, mask) {
+ var ret = value & mask;
+ if (ret !== value) {
+ abort('Function table mask error: function pointer is ' + value + ' which is masked by ' + mask + ', the likely cause of this is that the function pointer is being called by the wrong type.');
+ }
+ return ret;
+}
+
#endif
#endif
@@ -302,86 +310,168 @@ function assert(condition, text) {
var globalScope = this;
-// C calling interface. A convenient way to call C functions (in C files, or
-// defined with extern "C").
-//
-// Note: LLVM optimizations can inline and remove functions, after which you will not be
-// able to call them. Closure can also do so. To avoid that, add your function to
-// the exports using something like
-//
-// -s EXPORTED_FUNCTIONS='["_main", "_myfunc"]'
-//
-// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
-// @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and
-// 'array' for JavaScript arrays and typed arrays; note that arrays are 8-bit).
-// @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType,
-// except that 'array' is not possible (there is no way for us to know the length of the array)
-// @param args An array of the arguments to the function, as native JS values (as in returnType)
-// Note that string arguments will be stored on the stack (the JS string will become a C string on the stack).
-// @return The return value, as a native JS value (as in returnType)
-function ccall(ident, returnType, argTypes, args) {
- return ccallFunc(getCFunc(ident), returnType, argTypes, args);
-}
-Module["ccall"] = ccall;
-
// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)
function getCFunc(ident) {
- try {
- var func = Module['_' + ident]; // closure exported function
- if (!func) func = eval('_' + ident); // explicit lookup
- } catch(e) {
+ var func = Module['_' + ident]; // closure exported function
+ if (!func) {
+#if NO_DYNAMIC_EXECUTION == 0
+ try {
+ func = eval('_' + ident); // explicit lookup
+ } catch(e) {}
+#else
+ abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall/cwrap are not functional');
+#endif
}
assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)');
return func;
}
-// Internal function that does a C call using a function, not an identifier
-function ccallFunc(func, returnType, argTypes, args) {
+var cwrap, ccall;
+(function(){
var stack = 0;
- function toC(value, type) {
- if (type == 'string') {
- if (value === null || value === undefined || value === 0) return 0; // null string
- value = intArrayFromString(value);
- type = 'array';
- }
- if (type == 'array') {
- if (!stack) stack = Runtime.stackSave();
- var ret = Runtime.stackAlloc(value.length);
- writeArrayToMemory(value, ret);
+ var JSfuncs = {
+ 'stackSave' : function() {
+ stack = Runtime.stackSave();
+ },
+ 'stackRestore' : function() {
+ Runtime.stackRestore(stack);
+ },
+ // type conversion from js to c
+ 'arrayToC' : function(arr) {
+ var ret = Runtime.stackAlloc(arr.length);
+ writeArrayToMemory(arr, ret);
+ return ret;
+ },
+ 'stringToC' : function(str) {
+ var ret = 0;
+ if (str !== null && str !== undefined && str !== 0) { // null string
+ ret = Runtime.stackAlloc(str.length + 1); // +1 for the trailing '\0'
+ writeStringToMemory(str, ret);
+ }
return ret;
}
- return value;
- }
- function fromC(value, type) {
- if (type == 'string') {
- return Pointer_stringify(value);
+ };
+ // For fast lookup of conversion functions
+ var toC = {'string' : JSfuncs['stringToC'], 'array' : JSfuncs['arrayToC']};
+
+ // C calling interface. A convenient way to call C functions (in C files, or
+ // defined with extern "C").
+ //
+ // Note: ccall/cwrap use the C stack for temporary values. If you pass a string
+ // then it is only alive until the call is complete. If the code being
+ // called saves the pointer to be used later, it may point to invalid
+ // data. If you need a string to live forever, you can create it (and
+ // must later delete it manually!) using malloc and writeStringToMemory,
+ // for example.
+ //
+ // Note: LLVM optimizations can inline and remove functions, after which you will not be
+ // able to call them. Closure can also do so. To avoid that, add your function to
+ // the exports using something like
+ //
+ // -s EXPORTED_FUNCTIONS='["_main", "_myfunc"]'
+ //
+ // @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C")
+ // @param returnType The return type of the function, one of the JS types 'number', 'string' or 'array' (use 'number' for any C pointer, and
+ // 'array' for JavaScript arrays and typed arrays; note that arrays are 8-bit).
+ // @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType,
+ // except that 'array' is not possible (there is no way for us to know the length of the array)
+ // @param args An array of the arguments to the function, as native JS values (as in returnType)
+ // Note that string arguments will be stored on the stack (the JS string will become a C string on the stack).
+ // @return The return value, as a native JS value (as in returnType)
+ ccall = function ccallFunc(ident, returnType, argTypes, args) {
+ var func = getCFunc(ident);
+ var cArgs = [];
+#if ASSERTIONS
+ assert(returnType !== 'array', 'Return type should not be "array".');
+#endif
+ if (args) {
+ for (var i = 0; i < args.length; i++) {
+ var converter = toC[argTypes[i]];
+ if (converter) {
+ if (stack === 0) stack = Runtime.stackSave();
+ cArgs[i] = converter(args[i]);
+ } else {
+ cArgs[i] = args[i];
+ }
+ }
}
- assert(type != 'array');
- return value;
+ var ret = func.apply(null, cArgs);
+ if (returnType === 'string') ret = Pointer_stringify(ret);
+ if (stack !== 0) JSfuncs['stackRestore']();
+ return ret;
}
- var i = 0;
- var cArgs = args ? args.map(function(arg) {
- return toC(arg, argTypes[i++]);
- }) : [];
- var ret = fromC(func.apply(null, cArgs), returnType);
- if (stack) Runtime.stackRestore(stack);
- return ret;
-}
-// Returns a native JS wrapper for a C function. This is similar to ccall, but
-// returns a function you can call repeatedly in a normal way. For example:
-//
-// var my_function = cwrap('my_c_function', 'number', ['number', 'number']);
-// alert(my_function(5, 22));
-// alert(my_function(99, 12));
-//
-function cwrap(ident, returnType, argTypes) {
- var func = getCFunc(ident);
- return function() {
- return ccallFunc(func, returnType, argTypes, Array.prototype.slice.call(arguments));
+ var sourceRegex = /^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;
+ function parseJSFunc(jsfunc) {
+ // Match the body and the return value of a javascript function source
+ var parsed = jsfunc.toString().match(sourceRegex).slice(1);
+ return {arguments : parsed[0], body : parsed[1], returnValue: parsed[2]}
}
-}
+ var JSsource = {};
+ for (var fun in JSfuncs) {
+ if (JSfuncs.hasOwnProperty(fun)) {
+ // Elements of toCsource are arrays of three items:
+ // the code, and the return value
+ JSsource[fun] = parseJSFunc(JSfuncs[fun]);
+ }
+ }
+ // Returns a native JS wrapper for a C function. This is similar to ccall, but
+ // returns a function you can call repeatedly in a normal way. For example:
+ //
+ // var my_function = cwrap('my_c_function', 'number', ['number', 'number']);
+ // alert(my_function(5, 22));
+ // alert(my_function(99, 12));
+ //
+ cwrap = function cwrap(ident, returnType, argTypes) {
+ var cfunc = getCFunc(ident);
+ // When the function takes numbers and returns a number, we can just return
+ // the original function
+ var numericArgs = argTypes.every(function(type){ return type === 'number'});
+ var numericRet = (returnType !== 'string');
+ if ( numericRet && numericArgs) {
+ return cfunc;
+ }
+ // Creation of the arguments list (["$1","$2",...,"$nargs"])
+ var argNames = argTypes.map(function(x,i){return '$'+i});
+ var funcstr = "(function(" + argNames.join(',') + ") {";
+ var nargs = argTypes.length;
+ if (!numericArgs) {
+ // Generate the code needed to convert the arguments from javascript
+ // values to pointers
+ funcstr += JSsource['stackSave'].body + ';';
+ for (var i = 0; i < nargs; i++) {
+ var arg = argNames[i], type = argTypes[i];
+ if (type === 'number') continue;
+ var convertCode = JSsource[type + 'ToC']; // [code, return]
+ funcstr += 'var ' + convertCode.arguments + ' = ' + arg + ';';
+ funcstr += convertCode.body + ';';
+ funcstr += arg + '=' + convertCode.returnValue + ';';
+ }
+ }
+
+ // When the code is compressed, the name of cfunc is not literally 'cfunc' anymore
+ var cfuncname = parseJSFunc(function(){return cfunc}).returnValue;
+ // Call the function
+ funcstr += 'var ret = ' + cfuncname + '(' + argNames.join(',') + ');';
+ if (!numericRet) { // Return type can only by 'string' or 'number'
+ // Convert the result to a string
+ var strgfy = parseJSFunc(function(){return Pointer_stringify}).returnValue;
+ funcstr += 'ret = ' + strgfy + '(ret);';
+ }
+ if (!numericArgs) {
+ // If we had a stack, restore it
+ funcstr += JSsource['stackRestore'].body + ';';
+ }
+ funcstr += 'return ret})';
+#if NO_DYNAMIC_EXECUTION == 0
+ return eval(funcstr);
+#else
+ abort('NO_DYNAMIC_EXECUTION was set, cannot eval - ccall is not functional');
+#endif
+ };
+})();
Module["cwrap"] = cwrap;
+Module["ccall"] = ccall;
// Sets a value in memory in a dynamic way at run-time. Uses the
// type data. This is the same as makeSetValue, except that
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index 780a6d59..9e469ec4 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -17,6 +17,10 @@
typedef std::string ministring;
#endif
+// uncomment these out to get LLVM errs() debugging support
+//#include <llvm/Support/raw_ostream.h>
+//using namespace llvm;
+
template <class T, class U> static bool contains(const T& container, const U& contained) {
return container.count(contained);
}
@@ -202,6 +206,7 @@ void Block::Render(bool InLoop) {
if (Fused) {
PrintDebug("Fusing Multiple to Simple\n");
Parent->Next = Parent->Next->Next;
+ Fused->UseSwitch = false; // TODO: emit switches here
Fused->RenderLoopPrefix();
// When the Multiple has the same number of groups as we have branches,
@@ -243,7 +248,7 @@ void Block::Render(bool InLoop) {
Details = ProcessedBranchesOut[DefaultTarget];
}
bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel;
- bool HasFusedContent = Fused && contains(Fused->InnerMap, Target);
+ bool HasFusedContent = Fused && contains(Fused->InnerMap, Target->Id);
bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code;
if (iter != ProcessedBranchesOut.end()) {
// If there is nothing to show in this branch, omit the condition
@@ -286,7 +291,7 @@ void Block::Render(bool InLoop) {
if (!First) Indenter::Indent();
Details->Render(Target, SetCurrLabel);
if (HasFusedContent) {
- Fused->InnerMap.find(Target)->second->Render(InLoop);
+ Fused->InnerMap.find(Target->Id)->second->Render(InLoop);
} else if (Details->Type == Branch::Nested) {
// Nest the parent content here, and remove it from showing up afterwards as Next
assert(Parent->Next);
@@ -312,18 +317,24 @@ void Block::Render(bool InLoop) {
// MultipleShape
void MultipleShape::RenderLoopPrefix() {
- if (NeedLoop) {
- if (Labeled) {
- PrintIndented("L%d: do {\n", Id);
+ if (Breaks) {
+ if (UseSwitch) {
+ if (Labeled) {
+ PrintIndented("L%d: ", Id);
+ }
} else {
- PrintIndented("do {\n");
+ if (Labeled) {
+ PrintIndented("L%d: do {\n", Id);
+ } else {
+ PrintIndented("do {\n");
+ }
+ Indenter::Indent();
}
- Indenter::Indent();
}
}
void MultipleShape::RenderLoopPostfix() {
- if (NeedLoop) {
+ if (Breaks && !UseSwitch) {
Indenter::Unindent();
PrintIndented("} while(0);\n");
}
@@ -332,32 +343,41 @@ void MultipleShape::RenderLoopPostfix() {
void MultipleShape::Render(bool InLoop) {
RenderLoopPrefix();
- // We know that blocks with the same Id were split from the same source, so their contents are identical and they are logically the same, so re-merge them here
- typedef std::map<int, Shape*> IdShapeMap;
- IdShapeMap IdMap;
- for (BlockShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
- int Id = iter->first->Id;
- IdShapeMap::iterator Test = IdMap.find(Id);
- if (Test != IdMap.end()) {
- assert(Shape::IsSimple(iter->second) && Shape::IsSimple(Test->second)); // we can only merge simple blocks, something horrible has gone wrong if we see anything else
- continue;
+ if (!UseSwitch) {
+ // emit an if-else chain
+ bool First = true;
+ for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
+ if (AsmJS) {
+ PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first);
+ } else {
+ PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first);
+ }
+ First = false;
+ Indenter::Indent();
+ iter->second->Render(InLoop);
+ Indenter::Unindent();
+ PrintIndented("}\n");
}
- IdMap[iter->first->Id] = iter->second;
- }
-
- bool First = true;
- for (IdShapeMap::iterator iter = IdMap.begin(); iter != IdMap.end(); iter++) {
+ } else {
+ // emit a switch
if (AsmJS) {
- PrintIndented("%sif ((label|0) == %d) {\n", First ? "" : "else ", iter->first);
+ PrintIndented("switch (label|0) {\n");
} else {
- PrintIndented("%sif (label == %d) {\n", First ? "" : "else ", iter->first);
+ PrintIndented("switch (label) {\n");
}
- First = false;
Indenter::Indent();
- iter->second->Render(InLoop);
+ for (IdShapeMap::iterator iter = InnerMap.begin(); iter != InnerMap.end(); iter++) {
+ PrintIndented("case %d: {\n", iter->first);
+ Indenter::Indent();
+ iter->second->Render(InLoop);
+ PrintIndented("break;\n");
+ Indenter::Unindent();
+ PrintIndented("}\n");
+ }
Indenter::Unindent();
PrintIndented("}\n");
}
+
RenderLoopPostfix();
if (Next) Next->Render(InLoop);
}
@@ -547,7 +567,7 @@ void Relooper::Calculate(Block *Entry) {
PriorOut->Ancestor = Ancestor;
PriorOut->Type = Type;
if (MultipleShape *Multiple = Shape::IsMultiple(Ancestor)) {
- Multiple->NeedLoop++; // We are breaking out of this Multiple, so need a loop
+ Multiple->Breaks++; // We are breaking out of this Multiple, so need a loop
}
iter++; // carefully increment iter before erasing
Target->BranchesIn.erase(Prior);
@@ -853,7 +873,7 @@ void Relooper::Calculate(Block *Entry) {
iter = Next; // increment carefully because Solipsize can remove us
}
}
- Multiple->InnerMap[CurrEntry] = Process(CurrBlocks, CurrEntries, NULL);
+ Multiple->InnerMap[CurrEntry->Id] = Process(CurrBlocks, CurrEntries, NULL);
// If we are not fused, then our entries will actually be checked
if (!Fused) {
CurrEntry->IsCheckedMultipleEntry = true;
@@ -867,6 +887,11 @@ void Relooper::Calculate(Block *Entry) {
NextEntries.insert(Entry);
}
}
+ // The multiple has been created, we can decide how to implement it
+ if (Multiple->InnerMap.size() >= 10) {
+ Multiple->UseSwitch = true;
+ Multiple->Breaks++; // switch captures breaks
+ }
return Multiple;
}
@@ -1021,7 +1046,7 @@ void Relooper::Calculate(Block *Entry) {
PostOptimizer(Relooper *ParentInit) : Parent(ParentInit), Closure(NULL) {}
#define RECURSE_Multiple(shape, func) \
- for (BlockShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->InnerMap.end(); iter++) { \
+ for (IdShapeMap::iterator iter = shape->InnerMap.begin(); iter != shape->InnerMap.end(); iter++) { \
func(iter->second); \
}
#define RECURSE_Loop(shape, func) \
@@ -1042,7 +1067,7 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(S, {
Out.insert(Simple->Inner);
}, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
FollowNaturalFlow(iter->second, Out);
}
FollowNaturalFlow(Multiple->Next, Out);
@@ -1061,7 +1086,7 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(Root, {
}, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
FindNaturals(iter->second, Root->Natural);
}
}, {
@@ -1072,7 +1097,7 @@ void Relooper::Calculate(Block *Entry) {
// Remove unneeded breaks and continues.
// A flow operation is trivially unneeded if the shape we naturally get to by normal code
// execution is the same as the flow forces us to.
- void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL) {
+ void RemoveUnneededFlows(Shape *Root, Shape *Natural=NULL, LoopShape *LastLoop=NULL, unsigned Depth=0) {
BlockSet NaturalBlocks;
FollowNaturalFlow(Natural, NaturalBlocks);
Shape *Next = Root;
@@ -1083,7 +1108,7 @@ void Relooper::Calculate(Block *Entry) {
if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue)
if (Simple->Next) {
- if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2) {
+ if (!Simple->Inner->BranchVar && Simple->Inner->ProcessedBranchesOut.size() == 2 && Depth < 20) {
// If there is a next block, we already know at Simple creation time to make direct branches,
// and we can do nothing more in general. But, we try to optimize the case of a break and
// a direct: This would normally be if (break?) { break; } .. but if we
@@ -1111,7 +1136,7 @@ void Relooper::Calculate(Block *Entry) {
if (Details->Type == Branch::Break) {
Details->Type = Branch::Direct;
if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
- Multiple->NeedLoop--;
+ Multiple->Breaks--;
}
} else {
assert(Details->Type == Branch::Direct);
@@ -1119,6 +1144,7 @@ void Relooper::Calculate(Block *Entry) {
}
}
}
+ Depth++; // this optimization increases depth, for us and all our next chain (i.e., until this call returns)
}
Next = Simple->Next;
} else {
@@ -1130,24 +1156,24 @@ void Relooper::Calculate(Block *Entry) {
if (Details->Type != Branch::Direct && contains(NaturalBlocks, Target)) { // note: cannot handle split blocks
Details->Type = Branch::Direct;
if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
- Multiple->NeedLoop--;
+ Multiple->Breaks--;
}
} else if (Details->Type == Branch::Break && LastLoop && LastLoop->Natural == Details->Ancestor->Natural) {
// it is important to simplify breaks, as simpler breaks enable other optimizations
Details->Labeled = false;
if (MultipleShape *Multiple = Shape::IsMultiple(Details->Ancestor)) {
- Multiple->NeedLoop--;
+ Multiple->Breaks--;
}
}
}
}
}, {
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
- RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->NeedLoop ? NULL : LastLoop);
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ RemoveUnneededFlows(iter->second, Multiple->Next, Multiple->Breaks ? NULL : LastLoop, Depth+1);
}
Next = Multiple->Next;
}, {
- RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop);
+ RemoveUnneededFlows(Loop->Inner, Loop->Inner, Loop, Depth+1);
Next = Loop->Next;
});
}
@@ -1169,13 +1195,16 @@ void Relooper::Calculate(Block *Entry) {
SHAPE_SWITCH(Root, {
MultipleShape *Fused = Shape::IsMultiple(Root->Next);
// If we are fusing a Multiple with a loop into this Simple, then visit it now
- if (Fused && Fused->NeedLoop) {
+ if (Fused && Fused->Breaks) {
LoopStack.push(Fused);
}
if (Simple->Inner->BranchVar) {
LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy
}
if (Fused) {
+ if (Fused->UseSwitch) {
+ LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy
+ }
RECURSE_Multiple(Fused, FindLabeledLoops);
}
for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
@@ -1191,10 +1220,13 @@ void Relooper::Calculate(Block *Entry) {
}
}
}
+ if (Fused && Fused->UseSwitch) {
+ LoopStack.pop();
+ }
if (Simple->Inner->BranchVar) {
LoopStack.pop();
}
- if (Fused && Fused->NeedLoop) {
+ if (Fused && Fused->Breaks) {
LoopStack.pop();
}
if (Fused) {
@@ -1203,11 +1235,11 @@ void Relooper::Calculate(Block *Entry) {
Next = Root->Next;
}
}, {
- if (Multiple->NeedLoop) {
+ if (Multiple->Breaks) {
LoopStack.push(Multiple);
}
RECURSE(Multiple, FindLabeledLoops);
- if (Multiple->NeedLoop) {
+ if (Multiple->Breaks) {
LoopStack.pop();
}
Next = Root->Next;
@@ -1290,8 +1322,8 @@ void Debugging::Dump(Shape *S, const char *prefix) {
printf("<< Simple with block %d\n", Simple->Inner->Id);
}, {
printf("<< Multiple\n");
- for (BlockShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
- printf(" with entry %d\n", iter->first->Id);
+ for (IdShapeMap::iterator iter = Multiple->InnerMap.begin(); iter != Multiple->InnerMap.end(); iter++) {
+ printf(" with entry %d\n", iter->first);
}
}, {
printf("<< Loop\n");
diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h
index 152bae0e..c86e63ac 100644
--- a/src/relooper/Relooper.h
+++ b/src/relooper/Relooper.h
@@ -132,8 +132,6 @@ struct SimpleShape : public Shape {
}
};
-typedef std::map<Block*, Shape*> BlockShapeMap;
-
// A shape that may be implemented with a labeled loop.
struct LabeledShape : public Shape {
bool Labeled; // If we have a loop, whether it needs to be labeled
@@ -141,12 +139,16 @@ struct LabeledShape : public Shape {
LabeledShape(ShapeType TypeInit) : Shape(TypeInit), Labeled(false) {}
};
+// Blocks with the same id were split and are identical, so we just care about ids in Multiple entries
+typedef std::map<int, Shape*> IdShapeMap;
+
struct MultipleShape : public LabeledShape {
- BlockShapeMap InnerMap; // entry block -> shape
- int NeedLoop; // If we have branches, we need a loop. This is a counter of loop requirements,
- // if we optimize it to 0, the loop is unneeded
+ IdShapeMap InnerMap; // entry block ID -> shape
+ int Breaks; // If we have branches on us, we need a loop (or a switch). This is a counter of requirements,
+ // if we optimize it to 0, the loop is unneeded
+ bool UseSwitch; // Whether to switch on label as opposed to an if-else chain
- MultipleShape() : LabeledShape(Multiple), NeedLoop(0) {}
+ MultipleShape() : LabeledShape(Multiple), Breaks(0), UseSwitch(false) {}
void RenderLoopPrefix();
void RenderLoopPostfix();
diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py
index 18db997e..313f4d5e 100644
--- a/src/relooper/fuzzer.py
+++ b/src/relooper/fuzzer.py
@@ -3,7 +3,7 @@ import random, subprocess, difflib
while True:
# Random decisions
- num = random.randint(2, 250)
+ num = random.randint(2, 500)
density = random.random() * random.random()
decisions = [random.randint(1, num*20) for x in range(num*3)]
branches = [0]*num
@@ -123,14 +123,14 @@ int main() {
open('fuzz.slow.js', 'w').write(slow)
open('fuzz.cpp', 'w').write(fast)
print '_'
- slow_out = subprocess.Popen(['mozjs', '-m', '-n', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0]
+ slow_out = subprocess.Popen(['mozjs', 'fuzz.slow.js'], stdout=subprocess.PIPE).communicate()[0]
print '.'
subprocess.call(['g++', 'fuzz.cpp', 'Relooper.o', '-o', 'fuzz', '-g'])
print '*'
subprocess.call(['./fuzz'], stdout=open('fuzz.fast.js', 'w'))
print '-'
- fast_out = subprocess.Popen(['mozjs', '-m', '-n', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0]
+ fast_out = subprocess.Popen(['mozjs', 'fuzz.fast.js'], stdout=subprocess.PIPE).communicate()[0]
print
if slow_out != fast_out:
diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp
index 9f3ddceb..f319757b 100644
--- a/src/relooper/test.cpp
+++ b/src/relooper/test.cpp
@@ -1,4 +1,6 @@
+#include <vector>
+
#include "Relooper.h"
int main() {
@@ -435,5 +437,34 @@ int main() {
puts(r.GetOutputBuffer());
}
+
+ if (1) {
+ Relooper::MakeOutputBuffer(10);
+
+ printf("\n\n-- lots of exits to an unwind block, possible nesting --\n\n");
+
+ const int DEPTH = 40;
+
+ std::vector<Block*> blocks;
+ for (int i = 0; i < DEPTH; i++) blocks.push_back(new Block("// block\n", NULL));
+ Block *last = new Block("// last\nreturn;\n", NULL);
+ Block *UW = new Block("// UW\nresumeException();\n\n", NULL);
+
+ for (int i = 0; i < DEPTH; i++) {
+ Block *b = blocks[i];
+ b->AddBranchTo(i+1 < DEPTH ? blocks[i+1] : last, "check()", NULL);
+ b->AddBranchTo(UW, NULL, NULL);
+ }
+
+ Relooper r;
+ for (int i = 0; i < DEPTH; i++) r.AddBlock(blocks[i]);
+ r.AddBlock(last);
+ r.AddBlock(UW);
+
+ r.Calculate(blocks[0]);
+ r.Render();
+
+ puts(r.GetOutputBuffer());
+ }
}
diff --git a/src/relooper/test.txt b/src/relooper/test.txt
index d53aeeb1..2ae331c0 100644
--- a/src/relooper/test.txt
+++ b/src/relooper/test.txt
@@ -417,3 +417,156 @@
}
// block D
+
+
+-- lots of exits to an unwind block, possible nesting --
+
+ // block
+ do {
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (check()) {
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // block
+ if (!(check())) {
+ break;
+ }
+ // last
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } while(0);
+ // UW
+ resumeException();
+
+
diff --git a/src/runtime.js b/src/runtime.js
index 63610d3b..96b12294 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -96,6 +96,15 @@ function unInline(name_, params) {
}
var Runtime = {
+ // When a 64 bit long is returned from a compiled function the least significant
+ // 32 bit word is passed in the return value, but the most significant 32 bit
+ // word is placed in tempRet0. This provides an accessor for that value.
+ setTempRet0: function(value) {
+ tempRet0 = value;
+ },
+ getTempRet0: function() {
+ return tempRet0;
+ },
stackSave: function() {
return STACKTOP;
},
@@ -409,12 +418,16 @@ var Runtime = {
abort('invalid EM_ASM input |' + source + '|. Please use EM_ASM(..code..) (no quotes) or EM_ASM({ ..code($0).. }, input) (to input values)');
}
}
+#if NO_DYNAMIC_EXECUTION == 0
try {
var evalled = eval('(function(' + args.join(',') + '){ ' + source + ' })'); // new Function does not allow upvars in node
} catch(e) {
Module.printErr('error in executing inline EM_ASM code: ' + e + ' on: \n\n' + source + '\n\nwith args |' + args + '| (make sure to use the right one out of EM_ASM, EM_ASM_ARGS, etc.)');
throw e;
}
+#else
+ abort('NO_DYNAMIC_EXECUTION was set, cannot eval, so EM_ASM is not functional');
+#endif
return Runtime.asmConstCache[code] = evalled;
},
diff --git a/src/settings.js b/src/settings.js
index 8b046e95..bdb149e3 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -39,7 +39,7 @@ var CHECK_SIGNS = 0; // Runtime errors for signing issues that need correcting.
var ASSERTIONS = 1; // Whether we should add runtime assertions, for example to
// check that each allocation to the stack does not
- // exceed it's size, whether all allocations (stack and static) are
+ // exceed its size, whether all allocations (stack and static) are
// of positive size, etc., whether we should throw if we encounter a bad __label__, i.e.,
// if code flow runs into a fault
// ASSERTIONS == 2 gives even more runtime checks
@@ -124,13 +124,20 @@ var PRECISE_I32_MUL = 1; // If enabled, i32 multiplication is done with full pre
var PRECISE_F32 = 0; // 0: Use JS numbers for floating-point values. These are 64-bit and do not model C++
// floats exactly, which are 32-bit.
// 1: Model C++ floats precisely, using Math.fround, polyfilling when necessary. This
- // can be slow if the polyfill is used on heavy float32 computation.
+ // can be slow if the polyfill is used on heavy float32 computation. See note on
+ // browser support below.
// 2: Model C++ floats precisely using Math.fround if available in the JS engine, otherwise
// use an empty polyfill. This will have much less of a speed penalty than using the full
// polyfill in cases where engine support is not present. In addition, we can
// remove the empty polyfill calls themselves on the client when generating html,
// which should mean that this gives you the best of both worlds of 0 and 1, and is
// therefore recommended.
+ // XXX Note: To optimize float32-using code, we use the 'const' keyword in the emitted
+ // code. This allows us to avoid unnecessary calls to Math.fround, which would
+ // slow down engines not yet supporting that function. 'const' is present in
+ // all modern browsers, including Firefox, Chrome and Safari, but in IE is only
+ // present in IE11 and above. Therefore if you need to support legacy versions of
+ // IE, you should not enable PRECISE_F32 1 or 2.
var SIMD = 0; // Whether to emit SIMD code ( https://github.com/johnmccutchan/ecmascript_simd )
var CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output
@@ -338,7 +345,7 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc'];
var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can
// still eliminate functions as dead. This just exports them on the Module object.
var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This
- // is necessary to use the bindings generator with asm.js
+ // is necessary to use the WebIDL binder or bindings generator with asm.js
var RETAIN_COMPILER_SETTINGS = 0; // Remembers the values of these settings, and makes them accessible
// through Runtime.getCompilerSetting and emscripten_get_compiler_setting.
// To see what is retained, look for compilerSettings in the generated code.
@@ -495,6 +502,11 @@ var JS_CHUNK_SIZE = 10240; // Used as a maximum size before breaking up expressi
var EXPORT_NAME = 'Module'; // Global variable to export the module as for environments without a standardized module
// loading system (e.g. the browser and SM shell).
+var NO_DYNAMIC_EXECUTION = 0; // When enabled, we do not emit eval() and new Function(), which disables some functionality
+ // (causing runtime errors if attempted to be used), but allows the emitted code to be
+ // acceptable in places that disallow dynamic code execution (chrome packaged app, non-
+ // privileged firefox app, etc.)
+
var RUNNING_JS_OPTS = 0; // whether js opts will be run, after the main compiler
var COMPILER_ASSERTIONS = 0; // costly (slow) compile-time assertions
diff --git a/src/shell.html b/src/shell.html
index 226f12b9..d204bfa6 100644
--- a/src/shell.html
+++ b/src/shell.html
@@ -1245,7 +1245,16 @@
console.error(text);
}
},
- canvas: document.getElementById('canvas'),
+ canvas: (function() {
+ var canvas = document.getElementById('canvas');
+
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+ return canvas;
+ })(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.text) return;
@@ -1273,6 +1282,13 @@
}
};
Module.setStatus('Downloading...');
+ window.onerror = function() {
+ Module.setStatus('Exception thrown, see JavaScript console');
+ spinnerElement.style.display = 'none';
+ Module.setStatus = function(text) {
+ if (text) Module.printErr('[post-exception status] ' + text);
+ };
+ };
</script>
{{{ SCRIPT }}}
</body>
diff --git a/src/shell.js b/src/shell.js
index e1c0eb54..279a3461 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -96,7 +96,9 @@ else if (ENVIRONMENT_IS_SHELL) {
this['{{{ EXPORT_NAME }}}'] = Module;
+#if CLOSURE_COMPILER
eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly)
+#endif
}
else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
Module['read'] = function read(url) {
@@ -139,7 +141,11 @@ else {
}
function globalEval(x) {
+#if NO_DYNAMIC_EXECUTION == 0
eval.call(null, x);
+#else
+ throw 'NO_DYNAMIC_EXECUTION was set, cannot eval';
+#endif
}
if (!Module['load'] == 'undefined' && Module['read']) {
Module['load'] = function load(f) {
diff --git a/src/shell_minimal.html b/src/shell_minimal.html
index 6f483719..b67c6fdd 100644
--- a/src/shell_minimal.html
+++ b/src/shell_minimal.html
@@ -101,7 +101,16 @@
console.error(text);
}
},
- canvas: document.getElementById('canvas'),
+ canvas: (function() {
+ var canvas = document.getElementById('canvas');
+
+ // As a default initial behavior, pop up an alert when webgl context is lost. To make your
+ // application robust, you may want to override this behavior before shipping!
+ // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
+ canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
+
+ return canvas;
+ })(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.text) return;
@@ -129,6 +138,13 @@
}
};
Module.setStatus('Downloading...');
+ window.onerror = function() {
+ Module.setStatus('Exception thrown, see JavaScript console');
+ spinnerElement.style.display = 'none';
+ Module.setStatus = function(text) {
+ if (text) Module.printErr('[post-exception status] ' + text);
+ };
+ };
</script>
{{{ SCRIPT }}}
</body>
diff --git a/src/simd.js b/src/simd.js
index 6e3e3675..d5ff8b15 100644
--- a/src/simd.js
+++ b/src/simd.js
@@ -22,6 +22,9 @@
"use strict";
+// SIMD module.
+var SIMD = {};
+
/**
* Construct a new instance of float32x4 number.
* @param {double} value used for x lane.
@@ -30,9 +33,9 @@
* @param {double} value used for w lane.
* @constructor
*/
-function float32x4(x, y, z, w) {
- if (!(this instanceof float32x4)) {
- return new float32x4(x, y, z, w);
+SIMD.float32x4 = function(x, y, z, w) {
+ if (!(this instanceof SIMD.float32x4)) {
+ return new SIMD.float32x4(x, y, z, w);
}
this.storage_ = new Float32Array(4);
this.storage_[0] = x;
@@ -45,8 +48,8 @@ function float32x4(x, y, z, w) {
* Construct a new instance of float32x4 number with 0.0 in all lanes.
* @constructor
*/
-float32x4.zero = function() {
- return float32x4(0.0, 0.0, 0.0, 0.0);
+SIMD.float32x4.zero = function() {
+ return SIMD.float32x4(0.0, 0.0, 0.0, 0.0);
}
/**
@@ -55,38 +58,10 @@ float32x4.zero = function() {
* @param {double} value used for all lanes.
* @constructor
*/
-float32x4.splat = function(s) {
- return float32x4(s, s, s, s);
+SIMD.float32x4.splat = function(s) {
+ return SIMD.float32x4(s, s, s, s);
}
-Object.defineProperty(float32x4.prototype, 'x', {
- get: function() { return this.storage_[0]; }
-});
-
-Object.defineProperty(float32x4.prototype, 'y', {
- get: function() { return this.storage_[1]; }
-});
-
-Object.defineProperty(float32x4.prototype, 'z', {
- get: function() { return this.storage_[2]; }
-});
-
-Object.defineProperty(float32x4.prototype, 'w',
- { get: function() { return this.storage_[3]; }
-});
-
-/**
- * Extract the sign bit from each lane return them in the first 4 bits.
- */
-Object.defineProperty(float32x4.prototype, 'signMask', {
- get: function() {
- var mx = this.x < 0.0 ? 1 : 0;
- var my = this.y < 0.0 ? 1 : 0;
- var mz = this.z < 0.0 ? 1 : 0;
- var mw = this.w < 0.0 ? 1 : 0;
- return mx | my << 1 | mz << 2 | mw << 3;
- }
-});
/**
* Construct a new instance of int32x4 number.
@@ -96,9 +71,9 @@ Object.defineProperty(float32x4.prototype, 'signMask', {
* @param {integer} 32-bit unsigned value used for w lane.
* @constructor
*/
-function int32x4(x, y, z, w) {
- if (!(this instanceof int32x4)) {
- return new int32x4(x, y, z, w);
+SIMD.int32x4 = function(x, y, z, w) {
+ if (!(this instanceof SIMD.int32x4)) {
+ return new SIMD.int32x4(x, y, z, w);
}
this.storage_ = new Int32Array(4);
this.storage_[0] = x;
@@ -108,6 +83,14 @@ function int32x4(x, y, z, w) {
}
/**
+ * Construct a new instance of int32x4 number with 0 in all lanes.
+ * @constructor
+ */
+SIMD.int32x4.zero = function() {
+ return SIMD.int32x4(0, 0, 0, 0);
+}
+
+/**
* Construct a new instance of int32x4 number with 0xFFFFFFFF or 0x0 in each
* lane, depending on the truth value in x, y, z, and w.
* @param {boolean} flag used for x lane.
@@ -116,11 +99,11 @@ function int32x4(x, y, z, w) {
* @param {boolean} flag used for w lane.
* @constructor
*/
-int32x4.bool = function(x, y, z, w) {
- return int32x4(x ? -1 : 0x0,
- y ? -1 : 0x0,
- z ? -1 : 0x0,
- w ? -1 : 0x0);
+SIMD.int32x4.bool = function(x, y, z, w) {
+ return SIMD.int32x4(x ? -1 : 0x0,
+ y ? -1 : 0x0,
+ z ? -1 : 0x0,
+ w ? -1 : 0x0);
}
/**
@@ -129,746 +112,637 @@ int32x4.bool = function(x, y, z, w) {
* @param {integer} value used for all lanes.
* @constructor
*/
-int32x4.splat = function(s) {
- return int32x4(s, s, s, s);
+SIMD.int32x4.splat = function(s) {
+ return SIMD.int32x4(s, s, s, s);
}
-Object.defineProperty(int32x4.prototype, 'x', {
- get: function() { return this.storage_[0]; }
-});
+/**
+* @return {float32x4} New instance of float32x4 with absolute values of
+* t.
+*/
+SIMD.float32x4.abs = function(t) {
+ return SIMD.float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z),
+ Math.abs(t.w));
+}
-Object.defineProperty(int32x4.prototype, 'y', {
- get: function() { return this.storage_[1]; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with negated values of
+ * t.
+ */
+SIMD.float32x4.neg = function(t) {
+ return SIMD.float32x4(-t.x, -t.y, -t.z, -t.w);
+}
-Object.defineProperty(int32x4.prototype, 'z', {
- get: function() { return this.storage_[2]; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with a + b.
+ */
+SIMD.float32x4.add = function(a, b) {
+ return SIMD.float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
-Object.defineProperty(int32x4.prototype, 'w',
- { get: function() { return this.storage_[3]; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with a - b.
+ */
+SIMD.float32x4.sub = function(a, b) {
+ return SIMD.float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
-Object.defineProperty(int32x4.prototype, 'flagX', {
- get: function() { return this.storage_[0] != 0x0; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with a * b.
+ */
+SIMD.float32x4.mul = function(a, b) {
+ return SIMD.float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
+}
-Object.defineProperty(int32x4.prototype, 'flagY', {
- get: function() { return this.storage_[1] != 0x0; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with a / b.
+ */
+SIMD.float32x4.div = function(a, b) {
+ return SIMD.float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
+}
-Object.defineProperty(int32x4.prototype, 'flagZ', {
- get: function() { return this.storage_[2] != 0x0; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with t's values clamped
+ * between lowerLimit and upperLimit.
+ */
+SIMD.float32x4.clamp = function(t, lowerLimit, upperLimit) {
+ var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x;
+ var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y;
+ var cz = t.z < lowerLimit.z ? lowerLimit.z : t.z;
+ var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w;
+ cx = cx > upperLimit.x ? upperLimit.x : cx;
+ cy = cy > upperLimit.y ? upperLimit.y : cy;
+ cz = cz > upperLimit.z ? upperLimit.z : cz;
+ cw = cw > upperLimit.w ? upperLimit.w : cw;
+ return SIMD.float32x4(cx, cy, cz, cw);
+}
-Object.defineProperty(int32x4.prototype, 'flagW',
- { get: function() { return this.storage_[3] != 0x0; }
-});
+/**
+ * @return {float32x4} New instance of float32x4 with the minimum value of
+ * t and other.
+ */
+SIMD.float32x4.min = function(t, other) {
+ var cx = t.x > other.x ? other.x : t.x;
+ var cy = t.y > other.y ? other.y : t.y;
+ var cz = t.z > other.z ? other.z : t.z;
+ var cw = t.w > other.w ? other.w : t.w;
+ return SIMD.float32x4(cx, cy, cz, cw);
+}
/**
- * Extract the sign bit from each lane return them in the first 4 bits.
+ * @return {float32x4} New instance of float32x4 with the maximum value of
+ * t and other.
*/
-Object.defineProperty(int32x4.prototype, 'signMask', {
- get: function() {
- var mx = (this.storage_[0] & 0x80000000) >>> 31;
- var my = (this.storage_[1] & 0x80000000) >>> 31;
- var mz = (this.storage_[2] & 0x80000000) >>> 31;
- var mw = (this.storage_[3] & 0x80000000) >>> 31;
- return mx | my << 1 | mz << 2 | mw << 3;
- }
-});
+SIMD.float32x4.max = function(t, other) {
+ var cx = t.x < other.x ? other.x : t.x;
+ var cy = t.y < other.y ? other.y : t.y;
+ var cz = t.z < other.z ? other.z : t.z;
+ var cw = t.w < other.w ? other.w : t.w;
+ return SIMD.float32x4(cx, cy, cz, cw);
+}
-function isNumber(o) {
- return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
+/**
+ * @return {float32x4} New instance of float32x4 with reciprocal value of
+ * t.
+ */
+SIMD.float32x4.reciprocal = function(t) {
+ return SIMD.float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w);
}
-function isTypedArray(o) {
- return (o instanceof Int8Array) ||
- (o instanceof Uint8Array) ||
- (o instanceof Uint8ClampedArray) ||
- (o instanceof Int16Array) ||
- (o instanceof Uint16Array) ||
- (o instanceof Int32Array) ||
- (o instanceof Uint32Array) ||
- (o instanceof Float32Array) ||
- (o instanceof Float64Array) ||
- (o instanceof Float32x4Array);
+/**
+ * @return {float32x4} New instance of float32x4 with square root of the
+ * reciprocal value of t.
+ */
+SIMD.float32x4.reciprocalSqrt = function(t) {
+ return SIMD.float32x4(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y),
+ Math.sqrt(1.0 / t.z), Math.sqrt(1.0 / t.w));
+}
+/**
+ * @return {float32x4} New instance of float32x4 with values of t
+ * scaled by s.
+ */
+SIMD.float32x4.scale = function(t, s) {
+ return SIMD.float32x4(s * t.x, s * t.y, s * t.z, s * t.w);
}
-function isArrayBuffer(o) {
- return (o instanceof ArrayBuffer);
+/**
+ * @return {float32x4} New instance of float32x4 with square root of
+ * values of t.
+ */
+SIMD.float32x4.sqrt = function(t) {
+ return SIMD.float32x4(Math.sqrt(t.x), Math.sqrt(t.y),
+ Math.sqrt(t.z), Math.sqrt(t.w));
}
-function Float32x4Array(a, b, c) {
- if (isNumber(a)) {
- this.storage_ = new Float32Array(a*4);
- this.length_ = a;
- this.byteOffset_ = 0;
- return;
- } else if (isTypedArray(a)) {
- if (!(a instanceof Float32x4Array)) {
- throw "Copying typed array of non-Float32x4Array is unimplemented.";
- }
- this.storage_ = new Float32Array(a.length * 4);
- this.length_ = a.length;
- this.byteOffset_ = 0;
- // Copy floats.
- for (var i = 0; i < a.length*4; i++) {
- this.storage_[i] = a.storage_[i];
- }
- } else if (isArrayBuffer(a)) {
- if ((b != undefined) && (b % Float32x4Array.BYTES_PER_ELEMENT) != 0) {
- throw "byteOffset must be a multiple of 16.";
- }
- if (c != undefined) {
- c *= 4;
- this.storage_ = new Float32Array(a, b, c);
- }
- else {
- // Note: new Float32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined)
- this.storage_ = new Float32Array(a, b);
- }
- this.length_ = this.storage_.length / 4;
- this.byteOffset_ = b != undefined ? b : 0;
- } else {
- throw "Unknown type of first argument.";
- }
+/**
+ * @param {float32x4} t An instance of float32x4 to be shuffled.
+ * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
+ * @return {float32x4} New instance of float32x4 with lanes shuffled.
+ */
+SIMD.float32x4.shuffle = function(t, mask) {
+ var _x = (mask) & 0x3;
+ var _y = (mask >> 2) & 0x3;
+ var _z = (mask >> 4) & 0x3;
+ var _w = (mask >> 6) & 0x3;
+ return SIMD.float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z],
+ t.storage_[_w]);
}
-Object.defineProperty(Float32x4Array.prototype, 'length',
- { get: function() { return this.length_; }
-});
+/**
+ * @param {float32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result
+ * @param {float32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result
+ * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
+ * @return {float32x4} New instance of float32x4 with lanes shuffled.
+ */
+SIMD.float32x4.shuffleMix = function(t1, t2, mask) {
+ var _x = (mask) & 0x3;
+ var _y = (mask >> 2) & 0x3;
+ var _z = (mask >> 4) & 0x3;
+ var _w = (mask >> 6) & 0x3;
+ return SIMD.float32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z],
+ t2.storage_[_w]);
+}
-Object.defineProperty(Float32x4Array.prototype, 'byteLength',
- { get: function() { return this.length_ * Float32x4Array.BYTES_PER_ELEMENT; }
-});
+/**
+ * @param {double} value used for x lane.
+ * @return {float32x4} New instance of float32x4 with the values in t and
+ * x replaced with {x}.
+ */
+SIMD.float32x4.withX = function(t, x) {
+ return SIMD.float32x4(x, t.y, t.z, t.w);
+}
-Object.defineProperty(Float32x4Array, 'BYTES_PER_ELEMENT',
- { get: function() { return 16; }
-});
+/**
+ * @param {double} value used for y lane.
+ * @return {float32x4} New instance of float32x4 with the values in t and
+ * y replaced with {y}.
+ */
+SIMD.float32x4.withY = function(t, y) {
+ return SIMD.float32x4(t.x, y, t.z, t.w);
+}
-Object.defineProperty(Float32x4Array.prototype, 'BYTES_PER_ELEMENT',
- { get: function() { return 16; }
-});
+/**
+ * @param {double} value used for z lane.
+ * @return {float32x4} New instance of float32x4 with the values in t and
+ * z replaced with {z}.
+ */
+SIMD.float32x4.withZ = function(t, z) {
+ return SIMD.float32x4(t.x, t.y, z, t.w);
+}
-Object.defineProperty(Float32x4Array.prototype, 'byteOffset',
- { get: function() { return this.byteOffset_; }
-});
+/**
+ * @param {double} value used for w lane.
+ * @return {float32x4} New instance of float32x4 with the values in t and
+ * w replaced with {w}.
+ */
+SIMD.float32x4.withW = function(t, w) {
+ return SIMD.float32x4(t.x, t.y, t.z, w);
+}
-Object.defineProperty(Float32x4Array.prototype, 'buffer',
- { get: function() { return this.storage_.buffer; }
-});
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t < other.
+ */
+SIMD.float32x4.lessThan = function(t, other) {
+ var cx = t.x < other.x;
+ var cy = t.y < other.y;
+ var cz = t.z < other.z;
+ var cw = t.w < other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
-Float32x4Array.prototype.getAt = function(i) {
- if (i < 0) {
- throw "Index must be >= 0.";
- }
- if (i >= this.length) {
- throw "Index out of bounds.";
- }
- var x = this.storage_[i*4+0];
- var y = this.storage_[i*4+1];
- var z = this.storage_[i*4+2];
- var w = this.storage_[i*4+3];
- return float32x4(x, y, z, w);
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t <= other.
+ */
+SIMD.float32x4.lessThanOrEqual = function(t, other) {
+ var cx = t.x <= other.x;
+ var cy = t.y <= other.y;
+ var cz = t.z <= other.z;
+ var cw = t.w <= other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
}
-Float32x4Array.prototype.setAt = function(i, v) {
- if (i < 0) {
- throw "Index must be >= 0.";
- }
- if (i >= this.length) {
- throw "Index out of bounds.";
- }
- if (!(v instanceof float32x4)) {
- throw "Value is not a float32x4.";
- }
- this.storage_[i*4+0] = v.x;
- this.storage_[i*4+1] = v.y;
- this.storage_[i*4+2] = v.z;
- this.storage_[i*4+3] = v.w;
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t == other.
+ */
+SIMD.float32x4.equal = function(t, other) {
+ var cx = t.x == other.x;
+ var cy = t.y == other.y;
+ var cz = t.z == other.z;
+ var cw = t.w == other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
}
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t != other.
+ */
+SIMD.float32x4.notEqual = function(t, other) {
+ var cx = t.x != other.x;
+ var cy = t.y != other.y;
+ var cz = t.z != other.z;
+ var cw = t.w != other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
-function Int32x4Array(a, b, c) {
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t >= other.
+ */
+SIMD.float32x4.greaterThanOrEqual = function(t, other) {
+ var cx = t.x >= other.x;
+ var cy = t.y >= other.y;
+ var cz = t.z >= other.z;
+ var cw = t.w >= other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
- function isNumber(o) {
- return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
- }
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @param {float32x4} other An instance of float32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t > other.
+ */
+SIMD.float32x4.greaterThan = function(t, other) {
+ var cx = t.x > other.x;
+ var cy = t.y > other.y;
+ var cz = t.z > other.z;
+ var cw = t.w > other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
- function isTypedArray(o) {
- return (o instanceof Int8Array) ||
- (o instanceof Uint8Array) ||
- (o instanceof Uint8ClampedArray) ||
- (o instanceof Int16Array) ||
- (o instanceof Uint16Array) ||
- (o instanceof Int32Array) ||
- (o instanceof Uint32Array) ||
- (o instanceof Float32Array) ||
- (o instanceof Float64Array) ||
- (o instanceof Int32x4Array) ||
- (o instanceof Float32x4Array);
- }
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @return {int32x4} a bit-wise copy of t as a int32x4.
+ */
+SIMD.float32x4.bitsToInt32x4 = function(t) {
+ var alias = new Int32Array(t.storage_.buffer);
+ return SIMD.int32x4(alias[0], alias[1], alias[2], alias[3]);
+}
- function isArrayBuffer(o) {
- return (o instanceof ArrayBuffer);
- }
+/**
+ * @param {float32x4} t An instance of float32x4.
+ * @return {int32x4} with a integer to float conversion of t.
+ */
+SIMD.float32x4.toInt32x4 = function(t) {
+ var a = SIMD.int32x4(t.storage_[0], t.storage_[1], t.storage_[2],
+ t.storage_[3]);
+ return a;
+}
- if (isNumber(a)) {
- this.storage_ = new Int32Array(a*4);
- this.length_ = a;
- this.byteOffset_ = 0;
- return;
- } else if (isTypedArray(a)) {
- if (!(a instanceof Int32x4Array)) {
- throw "Copying typed array of non-Int32x4Array is unimplemented.";
- }
- this.storage_ = new Int32Array(a.length * 4);
- this.length_ = a.length;
- this.byteOffset_ = 0;
- // Copy floats.
- for (var i = 0; i < a.length*4; i++) {
- this.storage_[i] = a.storage_[i];
- }
- } else if (isArrayBuffer(a)) {
- if ((b != undefined) && (b % Int32x4Array.BYTES_PER_ELEMENT) != 0) {
- throw "byteOffset must be a multiple of 16.";
- }
- if (c != undefined) {
- c *= 4;
- this.storage_ = new Int32Array(a, b, c);
- }
- else {
- // Note: new Int32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined)
- this.storage_ = new Int32Array(a, b);
- }
- this.length_ = this.storage_.length / 4;
- this.byteOffset_ = b != undefined ? b : 0;
- } else {
- throw "Unknown type of first argument.";
- }
+/**
+ * @param {float32x4} a An instance of float32x4.
+ * @param {float32x4} b An instance of float32x4.
+ * @return {float32x4} New instance of float32x4 with values of a & b.
+ */
+SIMD.float32x4.and = function(a, b) {
+ var aInt = SIMD.float32x4.bitsToInt32x4(a);
+ var bInt = SIMD.float32x4.bitsToInt32x4(b);
+ return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.and(aInt, bInt));
}
-Object.defineProperty(Int32x4Array.prototype, 'length',
- { get: function() { return this.length_; }
-});
+/**
+ * @param {float32x4} a An instance of float32x4.
+ * @param {float32x4} b An instance of float32x4.
+ * @return {float32x4} New instance of float32x4 with values of a | b.
+ */
+SIMD.float32x4.or = function(a, b) {
+ var aInt = SIMD.float32x4.bitsToInt32x4(a);
+ var bInt = SIMD.float32x4.bitsToInt32x4(b);
+ return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(aInt, bInt));
+}
-Object.defineProperty(Int32x4Array.prototype, 'byteLength',
- { get: function() { return this.length_ * Int32x4Array.BYTES_PER_ELEMENT; }
-});
+/**
+ * @param {float32x4} a An instance of float32x4.
+ * @param {float32x4} b An instance of float32x4.
+ * @return {float32x4} New instance of float32x4 with values of a ^ b.
+ */
+SIMD.float32x4.xor = function(a, b) {
+ var aInt = SIMD.float32x4.bitsToInt32x4(a);
+ var bInt = SIMD.float32x4.bitsToInt32x4(b);
+ return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.xor(aInt, bInt));
+}
-Object.defineProperty(Int32x4Array, 'BYTES_PER_ELEMENT',
- { get: function() { return 16; }
-});
+/**
+ * @param {float32x4} a An instance of float32x4.
+ * @return {float32x4} New instance of float32x4 with values of ~a.
+ */
+SIMD.float32x4.not = function(a) {
+ var aInt = SIMD.float32x4.bitsToInt32x4(a);
+ return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.not(aInt));
+}
-Object.defineProperty(Int32x4Array.prototype, 'BYTES_PER_ELEMENT',
- { get: function() { return 16; }
-});
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a & b.
+ */
+SIMD.int32x4.and = function(a, b) {
+ return SIMD.int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
+}
-Object.defineProperty(Int32x4Array.prototype, 'byteOffset',
- { get: function() { return this.byteOffset_; }
-});
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a | b.
+ */
+SIMD.int32x4.or = function(a, b) {
+ return SIMD.int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
+}
-Object.defineProperty(Int32x4Array.prototype, 'buffer',
- { get: function() { return this.storage_.buffer; }
-});
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a ^ b.
+ */
+SIMD.int32x4.xor = function(a, b) {
+ return SIMD.int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
+}
-Int32x4Array.prototype.getAt = function(i) {
- if (i < 0) {
- throw "Index must be >= 0.";
- }
- if (i >= this.length) {
- throw "Index out of bounds.";
- }
- var x = this.storage_[i*4+0];
- var y = this.storage_[i*4+1];
- var z = this.storage_[i*4+2];
- var w = this.storage_[i*4+3];
- return float32x4(x, y, z, w);
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of ~t
+ */
+SIMD.int32x4.not = function(t) {
+ return SIMD.int32x4(~t.x, ~t.y, ~t.z, ~t.w);
}
-Int32x4Array.prototype.setAt = function(i, v) {
- if (i < 0) {
- throw "Index must be >= 0.";
- }
- if (i >= this.length) {
- throw "Index out of bounds.";
- }
- if (!(v instanceof int32x4)) {
- throw "Value is not a int32x4.";
- }
- this.storage_[i*4+0] = v.x;
- this.storage_[i*4+1] = v.y;
- this.storage_[i*4+2] = v.z;
- this.storage_[i*4+3] = v.w;
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of -t
+ */
+SIMD.int32x4.neg = function(t) {
+ return SIMD.int32x4(-t.x, -t.y, -t.z, -t.w);
}
-var SIMD = (function () {
- return {
- float32x4: {
- /**
- * @return {float32x4} New instance of float32x4 with absolute values of
- * t.
- */
- abs: function(t) {
- return new float32x4(Math.abs(t.x), Math.abs(t.y), Math.abs(t.z),
- Math.abs(t.w));
- },
- /**
- * @return {float32x4} New instance of float32x4 with negated values of
- * t.
- */
- neg: function(t) {
- return new float32x4(-t.x, -t.y, -t.z, -t.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with a + b.
- */
- add: function(a, b) {
- return new float32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with a - b.
- */
- sub: function(a, b) {
- return new float32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with a * b.
- */
- mul: function(a, b) {
- return new float32x4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with a / b.
- */
- div: function(a, b) {
- return new float32x4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with t's values clamped
- * between lowerLimit and upperLimit.
- */
- clamp: function(t, lowerLimit, upperLimit) {
- var cx = t.x < lowerLimit.x ? lowerLimit.x : t.x;
- var cy = t.y < lowerLimit.y ? lowerLimit.y : t.y;
- var cz = t.z < lowerLimit.z ? lowerLimit.z : t.z;
- var cw = t.w < lowerLimit.w ? lowerLimit.w : t.w;
- cx = cx > upperLimit.x ? upperLimit.x : cx;
- cy = cy > upperLimit.y ? upperLimit.y : cy;
- cz = cz > upperLimit.z ? upperLimit.z : cz;
- cw = cw > upperLimit.w ? upperLimit.w : cw;
- return new float32x4(cx, cy, cz, cw);
- },
- /**
- * @return {float32x4} New instance of float32x4 with the minimum value of
- * t and other.
- */
- min: function(t, other) {
- var cx = t.x > other.x ? other.x : t.x;
- var cy = t.y > other.y ? other.y : t.y;
- var cz = t.z > other.z ? other.z : t.z;
- var cw = t.w > other.w ? other.w : t.w;
- return new float32x4(cx, cy, cz, cw);
- },
- /**
- * @return {float32x4} New instance of float32x4 with the maximum value of
- * t and other.
- */
- max: function(t, other) {
- var cx = t.x < other.x ? other.x : t.x;
- var cy = t.y < other.y ? other.y : t.y;
- var cz = t.z < other.z ? other.z : t.z;
- var cw = t.w < other.w ? other.w : t.w;
- return new float32x4(cx, cy, cz, cw);
- },
- /**
- * @return {float32x4} New instance of float32x4 with reciprocal value of
- * t.
- */
- reciprocal: function(t) {
- return new float32x4(1.0 / t.x, 1.0 / t.y, 1.0 / t.z, 1.0 / t.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with square root of the
- * reciprocal value of t.
- */
- reciprocalSqrt: function(t) {
- return new float32x4(Math.sqrt(1.0 / t.x), Math.sqrt(1.0 / t.y),
- Math.sqrt(1.0 / t.z), Math.sqrt(1.0 / t.w));
- },
- /**
- * @return {float32x4} New instance of float32x4 with values of t
- * scaled by s.
- */
- scale: function(t, s) {
- return new float32x4(s * t.x, s * t.y, s * t.z, s * t.w);
- },
- /**
- * @return {float32x4} New instance of float32x4 with square root of
- * values of t.
- */
- sqrt: function(t) {
- return new float32x4(Math.sqrt(t.x), Math.sqrt(t.y),
- Math.sqrt(t.z), Math.sqrt(t.w));
- },
- /**
- * @param {float32x4} t An instance of float32x4 to be shuffled.
- * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
- * @return {float32x4} New instance of float32x4 with lanes shuffled.
- */
- shuffle: function(t, mask) {
- var _x = (mask) & 0x3;
- var _y = (mask >> 2) & 0x3;
- var _z = (mask >> 4) & 0x3;
- var _w = (mask >> 6) & 0x3;
- return new float32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z],
- t.storage_[_w]);
- },
- /**
- * @param {float32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result
- * @param {float32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result
- * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
- * @return {float32x4} New instance of float32x4 with lanes shuffled.
- */
- shuffleMix: function(t1, t2, mask) {
- var _x = (mask) & 0x3;
- var _y = (mask >> 2) & 0x3;
- var _z = (mask >> 4) & 0x3;
- var _w = (mask >> 6) & 0x3;
- return new float32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z],
- t2.storage_[_w]);
- },
- /**
- * @param {double} value used for x lane.
- * @return {float32x4} New instance of float32x4 with the values in t and
- * x replaced with {x}.
- */
- withX: function(t, x) {
- return new float32x4(x, t.y, t.z, t.w);
- },
- /**
- * @param {double} value used for y lane.
- * @return {float32x4} New instance of float32x4 with the values in t and
- * y replaced with {y}.
- */
- withY: function(t, y) {
- return new float32x4(t.x, y, t.z, t.w);
- },
- /**
- * @param {double} value used for z lane.
- * @return {float32x4} New instance of float32x4 with the values in t and
- * z replaced with {z}.
- */
- withZ: function(t, z) {
- return new float32x4(t.x, t.y, z, t.w);
- },
- /**
- * @param {double} value used for w lane.
- * @return {float32x4} New instance of float32x4 with the values in t and
- * w replaced with {w}.
- */
- withW: function(t, w) {
- return new float32x4(t.x, t.y, t.z, w);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t < other.
- */
- lessThan: function(t, other) {
- var cx = t.x < other.x;
- var cy = t.y < other.y;
- var cz = t.z < other.z;
- var cw = t.w < other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t <= other.
- */
- lessThanOrEqual: function(t, other) {
- var cx = t.x <= other.x;
- var cy = t.y <= other.y;
- var cz = t.z <= other.z;
- var cw = t.w <= other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t == other.
- */
- equal: function(t, other) {
- var cx = t.x == other.x;
- var cy = t.y == other.y;
- var cz = t.z == other.z;
- var cw = t.w == other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t != other.
- */
- notEqual: function(t, other) {
- var cx = t.x != other.x;
- var cy = t.y != other.y;
- var cz = t.z != other.z;
- var cw = t.w != other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t >= other.
- */
- greaterThanOrEqual: function(t, other) {
- var cx = t.x >= other.x;
- var cy = t.y >= other.y;
- var cz = t.z >= other.z;
- var cw = t.w >= other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @param {float32x4} other An instance of float32x4.
- * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
- * the result of t > other.
- */
- greaterThan: function(t, other) {
- var cx = t.x > other.x;
- var cy = t.y > other.y;
- var cz = t.z > other.z;
- var cw = t.w > other.w;
- return int32x4.bool(cx, cy, cz, cw);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @return {int32x4} a bit-wise copy of t as a int32x4.
- */
- bitsToInt32x4: function(t) {
- var alias = new Int32Array(t.storage_.buffer);
- return new int32x4(alias[0], alias[1], alias[2], alias[3]);
- },
- /**
- * @param {float32x4} t An instance of float32x4.
- * @return {int32x4} with a integer to float conversion of t.
- */
- toInt32x4: function(t) {
- var a = new int32x4(t.storage_[0], t.storage_[1], t.storage_[2],
- t.storage_[3]);
- return a;
- }
- },
- int32x4: {
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a & b.
- */
- and: function(a, b) {
- return new int32x4(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w);
- },
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a | b.
- */
- or: function(a, b) {
- return new int32x4(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w);
- },
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a ^ b.
- */
- xor: function(a, b) {
- return new int32x4(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of ~t
- */
- not: function(t) {
- return new int32x4(~t.x, ~t.y, ~t.z, ~t.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of -t
- */
- neg: function(t) {
- return new int32x4(-t.x, -t.y, -t.z, -t.w);
- },
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a + b.
- */
- add: function(a, b) {
- return new int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
- },
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a - b.
- */
- sub: function(a, b) {
- return new int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
- },
- /**
- * @param {int32x4} a An instance of int32x4.
- * @param {int32x4} b An instance of int32x4.
- * @return {int32x4} New instance of int32x4 with values of a * b.
- */
- mul: function(a, b) {
- return new int32x4(Math.imul(a.x, b.x), Math.imul(a.y, b.y),
- Math.imul(a.z, b.z), Math.imul(a.w, b.w));
- },
- /**
- * @param {int32x4} t An instance of float32x4 to be shuffled.
- * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
- * @return {int32x4} New instance of float32x4 with lanes shuffled.
- */
- shuffle: function(t, mask) {
- var _x = (mask) & 0x3;
- var _y = (mask >> 2) & 0x3;
- var _z = (mask >> 4) & 0x3;
- var _w = (mask >> 6) & 0x3;
- return new int32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z],
- t.storage_[_w]);
- },
- /**
- * @param {int32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result
- * @param {int32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result
- * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
- * @return {int32x4} New instance of float32x4 with lanes shuffled.
- */
- shuffleMix: function(t1, t2, mask) {
- var _x = (mask) & 0x3;
- var _y = (mask >> 2) & 0x3;
- var _z = (mask >> 4) & 0x3;
- var _w = (mask >> 6) & 0x3;
- return new int32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z],
- t2.storage_[_w]);
- },
- /**
- * @param {float32x4}
- */
- select: function(t, trueValue, falseValue) {
- var tv = SIMD.float32x4.bitsToInt32x4(trueValue);
- var fv = SIMD.float32x4.bitsToInt32x4(falseValue);
- var tr = SIMD.int32x4.and(t, tv);
- var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), fv);
- return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(tr, fr));
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {integer} 32-bit value used for x lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * x lane replaced with {x}.
- */
- withX: function(t, x) {
- return new int32x4(x, t.y, t.z, t.w);
- },
- /**
- * param {int32x4} t An instance of int32x4.
- * @param {integer} 32-bit value used for y lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * y lane replaced with {y}.
- */
- withY: function(t, y) {
- return new int32x4(t.x, y, t.z, t.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {integer} 32-bit value used for z lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * z lane replaced with {z}.
- */
- withZ: function(t, z) {
- return new int32x4(t.x, t.y, z, t.w);
- },
- /**
- * @param {integer} 32-bit value used for w lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * w lane replaced with {w}.
- */
- withW: function(t, w) {
- return new int32x4(t.x, t.y, t.z, w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {boolean} x flag used for x lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * x lane replaced with {x}.
- */
- withFlagX: function(t, flagX) {
- var x = flagX ? 0xFFFFFFFF : 0x0;
- return new int32x4(x, t.y, t.z, t.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {boolean} y flag used for y lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * y lane replaced with {y}.
- */
- withFlagY: function(t, flagY) {
- var y = flagY ? 0xFFFFFFFF : 0x0;
- return new int32x4(t.x, y, t.z, t.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {boolean} z flag used for z lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * z lane replaced with {z}.
- */
- withFlagZ: function(t, flagZ) {
- var z = flagZ ? 0xFFFFFFFF : 0x0;
- return new int32x4(t.x, t.y, z, t.w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @param {boolean} w flag used for w lane.
- * @return {int32x4} New instance of int32x4 with the values in t and
- * w lane replaced with {w}.
- */
- withFlagW: function(t, flagW) {
- var w = flagW ? 0xFFFFFFFF : 0x0;
- return new int32x4(t.x, t.y, t.z, w);
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @return {float32x4} a bit-wise copy of t as a float32x4.
- */
- bitsToFloat32x4: function(t) {
- var temp_storage = new Int32Array([t.storage_[0], t.storage_[1], t.storage_[2], t.storage_[3]]);
- var alias = new Float32Array(temp_storage.buffer);
- var fx4 = float32x4.zero();
- fx4.storage_ = alias;
- return fx4;
- },
- /**
- * @param {int32x4} t An instance of int32x4.
- * @return {float32x4} with a float to integer conversion copy of t.
- */
- toFloat32x4: function(t) {
- var a = float32x4.zero();
- a.storage_[0] = t.storage_[0];
- a.storage_[1] = t.storage_[1];
- a.storage_[2] = t.storage_[2];
- a.storage_[3] = t.storage_[3];
- return a;
- }
- }
- }
-})();
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a + b.
+ */
+SIMD.int32x4.add = function(a, b) {
+ return SIMD.int32x4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a - b.
+ */
+SIMD.int32x4.sub = function(a, b) {
+ return SIMD.int32x4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int32x4} b An instance of int32x4.
+ * @return {int32x4} New instance of int32x4 with values of a * b.
+ */
+SIMD.int32x4.mul = function(a, b) {
+ return SIMD.int32x4(Math.imul(a.x, b.x), Math.imul(a.y, b.y),
+ Math.imul(a.z, b.z), Math.imul(a.w, b.w));
+}
+
+/**
+ * @param {int32x4} t An instance of float32x4 to be shuffled.
+ * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
+ * @return {int32x4} New instance of float32x4 with lanes shuffled.
+ */
+SIMD.int32x4.shuffle = function(t, mask) {
+ var _x = (mask) & 0x3;
+ var _y = (mask >> 2) & 0x3;
+ var _z = (mask >> 4) & 0x3;
+ var _w = (mask >> 6) & 0x3;
+ return SIMD.int32x4(t.storage_[_x], t.storage_[_y], t.storage_[_z],
+ t.storage_[_w]);
+}
+
+/**
+ * @param {int32x4} t1 An instance of float32x4 to be shuffled. XY lanes in result
+ * @param {int32x4} t2 An instance of float32x4 to be shuffled. ZW lanes in result
+ * @param {integer} mask One of the 256 shuffle masks, for example, SIMD.XXXX.
+ * @return {int32x4} New instance of float32x4 with lanes shuffled.
+ */
+SIMD.int32x4.shuffleMix = function(t1, t2, mask) {
+ var _x = (mask) & 0x3;
+ var _y = (mask >> 2) & 0x3;
+ var _z = (mask >> 4) & 0x3;
+ var _w = (mask >> 6) & 0x3;
+ return SIMD.int32x4(t1.storage_[_x], t1.storage_[_y], t2.storage_[_z],
+ t2.storage_[_w]);
+}
+
+/**
+ * @param {float32x4}
+ */
+SIMD.int32x4.select = function(t, trueValue, falseValue) {
+ var tv = SIMD.float32x4.bitsToInt32x4(trueValue);
+ var fv = SIMD.float32x4.bitsToInt32x4(falseValue);
+ var tr = SIMD.int32x4.and(t, tv);
+ var fr = SIMD.int32x4.and(SIMD.int32x4.not(t), fv);
+ return SIMD.int32x4.bitsToFloat32x4(SIMD.int32x4.or(tr, fr));
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {integer} 32-bit value used for x lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * x lane replaced with {x}.
+ */
+SIMD.int32x4.withX = function(t, x) {
+ return SIMD.int32x4(x, t.y, t.z, t.w);
+}
+
+/**
+ * param {int32x4} t An instance of int32x4.
+ * @param {integer} 32-bit value used for y lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * y lane replaced with {y}.
+ */
+SIMD.int32x4.withY = function(t, y) {
+ return SIMD.int32x4(t.x, y, t.z, t.w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {integer} 32-bit value used for z lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * z lane replaced with {z}.
+ */
+SIMD.int32x4.withZ = function(t, z) {
+ return SIMD.int32x4(t.x, t.y, z, t.w);
+}
+
+/**
+ * @param {integer} 32-bit value used for w lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * w lane replaced with {w}.
+ */
+SIMD.int32x4.withW = function(t, w) {
+ return SIMD.int32x4(t.x, t.y, t.z, w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {boolean} x flag used for x lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * x lane replaced with {x}.
+ */
+SIMD.int32x4.withFlagX = function(t, flagX) {
+ var x = flagX ? 0xFFFFFFFF : 0x0;
+ return SIMD.int32x4(x, t.y, t.z, t.w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {boolean} y flag used for y lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * y lane replaced with {y}.
+ */
+SIMD.int32x4.withFlagY = function(t, flagY) {
+ var y = flagY ? 0xFFFFFFFF : 0x0;
+ return SIMD.int32x4(t.x, y, t.z, t.w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {boolean} z flag used for z lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * z lane replaced with {z}.
+ */
+SIMD.int32x4.withFlagZ = function(t, flagZ) {
+ var z = flagZ ? 0xFFFFFFFF : 0x0;
+ return SIMD.int32x4(t.x, t.y, z, t.w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {boolean} w flag used for w lane.
+ * @return {int32x4} New instance of int32x4 with the values in t and
+ * w lane replaced with {w}.
+ */
+SIMD.int32x4.withFlagW = function(t, flagW) {
+ var w = flagW ? 0xFFFFFFFF : 0x0;
+ return SIMD.int32x4(t.x, t.y, t.z, w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {int32x4} other An instance of int32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t == other.
+ */
+SIMD.int32x4.equal = function(t, other) {
+ var cx = t.x == other.x;
+ var cy = t.y == other.y;
+ var cz = t.z == other.z;
+ var cw = t.w == other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {int32x4} other An instance of int32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t > other.
+ */
+SIMD.int32x4.greaterThan = function(t, other) {
+ var cx = t.x > other.x;
+ var cy = t.y > other.y;
+ var cz = t.z > other.z;
+ var cw = t.w > other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @param {int32x4} other An instance of int32x4.
+ * @return {int32x4} 0xFFFFFFFF or 0x0 in each lane depending on
+ * the result of t < other.
+ */
+SIMD.int32x4.lessThan = function(t, other) {
+ var cx = t.x < other.x;
+ var cy = t.y < other.y;
+ var cz = t.z < other.z;
+ var cw = t.w < other.w;
+ return SIMD.int32x4.bool(cx, cy, cz, cw);
+}
+
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int} bits Bit count to shift by.
+ * @return {int32x4} lanes in a shifted by bits.
+ */
+SIMD.int32x4.shiftLeft = function(a, bits) {
+ var x = a.x << bits;
+ var y = a.y << bits;
+ var z = a.z << bits;
+ var w = a.w << bits;
+ return SIMD.int32x4(x, y, z, w);
+}
+
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int} bits Bit count to shift by.
+ * @return {int32x4} lanes in a shifted by bits.
+ */
+SIMD.int32x4.shiftRightLogical = function(a, bits) {
+ var x = a.x >>> bits;
+ var y = a.y >>> bits;
+ var z = a.z >>> bits;
+ var w = a.w >>> bits;
+ return SIMD.int32x4(x, y, z, w);
+}
+
+/**
+ * @param {int32x4} a An instance of int32x4.
+ * @param {int} bits Bit count to shift by.
+ * @return {int32x4} lanes in a shifted by bits.
+ */
+SIMD.int32x4.shiftRightArithmetic = function(a, bits) {
+ var x = a.x >> bits;
+ var y = a.y >> bits;
+ var z = a.z >> bits;
+ var w = a.w >> bits;
+ return SIMD.int32x4(x, y, z, w);
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @return {float32x4} a bit-wise copy of t as a float32x4.
+ */
+SIMD.int32x4.bitsToFloat32x4 = function(t) {
+ var temp_storage = new Int32Array([t.storage_[0], t.storage_[1], t.storage_[2], t.storage_[3]]);
+ var alias = new Float32Array(temp_storage.buffer);
+ var fx4 = SIMD.float32x4.zero();
+ fx4.storage_ = alias;
+ return fx4;
+}
+
+/**
+ * @param {int32x4} t An instance of int32x4.
+ * @return {float32x4} with a float to integer conversion copy of t.
+ */
+SIMD.int32x4.toFloat32x4 = function(t) {
+ var a = float32x4.zero();
+ a.storage_[0] = t.storage_[0];
+ a.storage_[1] = t.storage_[1];
+ a.storage_[2] = t.storage_[2];
+ a.storage_[3] = t.storage_[3];
+ return a;
+}
Object.defineProperty(SIMD, 'XXXX', { get: function() { return 0x0; } });
Object.defineProperty(SIMD, 'XXXY', { get: function() { return 0x40; } });
@@ -1126,3 +1000,302 @@ Object.defineProperty(SIMD, 'WWWX', { get: function() { return 0x3F; } });
Object.defineProperty(SIMD, 'WWWY', { get: function() { return 0x7F; } });
Object.defineProperty(SIMD, 'WWWZ', { get: function() { return 0xBF; } });
Object.defineProperty(SIMD, 'WWWW', { get: function() { return 0xFF; } });
+
+Object.defineProperty(SIMD.float32x4.prototype, 'x', {
+ get: function() { return this.storage_[0]; }
+});
+
+Object.defineProperty(SIMD.float32x4.prototype, 'y', {
+ get: function() { return this.storage_[1]; }
+});
+
+Object.defineProperty(SIMD.float32x4.prototype, 'z', {
+ get: function() { return this.storage_[2]; }
+});
+
+Object.defineProperty(SIMD.float32x4.prototype, 'w',
+ { get: function() { return this.storage_[3]; }
+});
+
+/**
+ * Extract the sign bit from each lane return them in the first 4 bits.
+ */
+Object.defineProperty(SIMD.float32x4.prototype, 'signMask', {
+ get: function() {
+ var mx = this.x < 0.0 ? 1 : 0;
+ var my = this.y < 0.0 ? 1 : 0;
+ var mz = this.z < 0.0 ? 1 : 0;
+ var mw = this.w < 0.0 ? 1 : 0;
+ return mx | my << 1 | mz << 2 | mw << 3;
+ }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'x', {
+ get: function() { return this.storage_[0]; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'y', {
+ get: function() { return this.storage_[1]; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'z', {
+ get: function() { return this.storage_[2]; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'w',
+ { get: function() { return this.storage_[3]; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'flagX', {
+ get: function() { return this.storage_[0] != 0x0; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'flagY', {
+ get: function() { return this.storage_[1] != 0x0; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'flagZ', {
+ get: function() { return this.storage_[2] != 0x0; }
+});
+
+Object.defineProperty(SIMD.int32x4.prototype, 'flagW',
+ { get: function() { return this.storage_[3] != 0x0; }
+});
+
+/**
+ * Extract the sign bit from each lane return them in the first 4 bits.
+ */
+Object.defineProperty(SIMD.int32x4.prototype, 'signMask', {
+ get: function() {
+ var mx = (this.storage_[0] & 0x80000000) >>> 31;
+ var my = (this.storage_[1] & 0x80000000) >>> 31;
+ var mz = (this.storage_[2] & 0x80000000) >>> 31;
+ var mw = (this.storage_[3] & 0x80000000) >>> 31;
+ return mx | my << 1 | mz << 2 | mw << 3;
+ }
+});
+
+function isNumber(o) {
+ return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
+}
+
+function isTypedArray(o) {
+ return (o instanceof Int8Array) ||
+ (o instanceof Uint8Array) ||
+ (o instanceof Uint8ClampedArray) ||
+ (o instanceof Int16Array) ||
+ (o instanceof Uint16Array) ||
+ (o instanceof Int32Array) ||
+ (o instanceof Uint32Array) ||
+ (o instanceof Float32Array) ||
+ (o instanceof Float64Array) ||
+ (o instanceof Float32x4Array);
+}
+
+function isArrayBuffer(o) {
+ return (o instanceof ArrayBuffer);
+}
+
+function Float32x4Array(a, b, c) {
+ if (isNumber(a)) {
+ this.storage_ = new Float32Array(a*4);
+ this.length_ = a;
+ this.byteOffset_ = 0;
+ return;
+ } else if (isTypedArray(a)) {
+ if (!(a instanceof Float32x4Array)) {
+ throw "Copying typed array of non-Float32x4Array is unimplemented.";
+ }
+ this.storage_ = new Float32Array(a.length * 4);
+ this.length_ = a.length;
+ this.byteOffset_ = 0;
+ // Copy floats.
+ for (var i = 0; i < a.length*4; i++) {
+ this.storage_[i] = a.storage_[i];
+ }
+ } else if (isArrayBuffer(a)) {
+ if ((b != undefined) && (b % Float32x4Array.BYTES_PER_ELEMENT) != 0) {
+ throw "byteOffset must be a multiple of 16.";
+ }
+ if (c != undefined) {
+ c *= 4;
+ this.storage_ = new Float32Array(a, b, c);
+ }
+ else {
+ // Note = new Float32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined)
+ this.storage_ = new Float32Array(a, b);
+ }
+ this.length_ = this.storage_.length / 4;
+ this.byteOffset_ = b != undefined ? b : 0;
+ } else {
+ throw "Unknown type of first argument.";
+ }
+}
+
+Object.defineProperty(Float32x4Array.prototype, 'length',
+ { get: function() { return this.length_; }
+});
+
+Object.defineProperty(Float32x4Array.prototype, 'byteLength',
+ { get: function() { return this.length_ * Float32x4Array.BYTES_PER_ELEMENT; }
+});
+
+Object.defineProperty(Float32x4Array, 'BYTES_PER_ELEMENT',
+ { get: function() { return 16; }
+});
+
+Object.defineProperty(Float32x4Array.prototype, 'BYTES_PER_ELEMENT',
+ { get: function() { return 16; }
+});
+
+Object.defineProperty(Float32x4Array.prototype, 'byteOffset',
+ { get: function() { return this.byteOffset_; }
+});
+
+Object.defineProperty(Float32x4Array.prototype, 'buffer',
+ { get: function() { return this.storage_.buffer; }
+});
+
+Float32x4Array.prototype.getAt = function(i) {
+ if (i < 0) {
+ throw "Index must be >= 0.";
+ }
+ if (i >= this.length) {
+ throw "Index out of bounds.";
+ }
+ var x = this.storage_[i*4+0];
+ var y = this.storage_[i*4+1];
+ var z = this.storage_[i*4+2];
+ var w = this.storage_[i*4+3];
+ return SIMD.float32x4(x, y, z, w);
+}
+
+Float32x4Array.prototype.setAt = function(i, v) {
+ if (i < 0) {
+ throw "Index must be >= 0.";
+ }
+ if (i >= this.length) {
+ throw "Index out of bounds.";
+ }
+ if (!(v instanceof SIMD.float32x4)) {
+ throw "Value is not a float32x4.";
+ }
+ this.storage_[i*4+0] = v.x;
+ this.storage_[i*4+1] = v.y;
+ this.storage_[i*4+2] = v.z;
+ this.storage_[i*4+3] = v.w;
+}
+
+
+function Int32x4Array(a, b, c) {
+
+ function isNumber(o) {
+ return typeof o == "number" || (typeof o == "object" && o.constructor === Number);
+ }
+
+ function isTypedArray(o) {
+ return (o instanceof Int8Array) ||
+ (o instanceof Uint8Array) ||
+ (o instanceof Uint8ClampedArray) ||
+ (o instanceof Int16Array) ||
+ (o instanceof Uint16Array) ||
+ (o instanceof Int32Array) ||
+ (o instanceof Uint32Array) ||
+ (o instanceof Float32Array) ||
+ (o instanceof Float64Array) ||
+ (o instanceof Int32x4Array) ||
+ (o instanceof Float32x4Array);
+ }
+
+ function isArrayBuffer(o) {
+ return (o instanceof ArrayBuffer);
+ }
+
+ if (isNumber(a)) {
+ this.storage_ = new Int32Array(a*4);
+ this.length_ = a;
+ this.byteOffset_ = 0;
+ return;
+ } else if (isTypedArray(a)) {
+ if (!(a instanceof Int32x4Array)) {
+ throw "Copying typed array of non-Int32x4Array is unimplemented.";
+ }
+ this.storage_ = new Int32Array(a.length * 4);
+ this.length_ = a.length;
+ this.byteOffset_ = 0;
+ // Copy ints.
+ for (var i = 0; i < a.length*4; i++) {
+ this.storage_[i] = a.storage_[i];
+ }
+ } else if (isArrayBuffer(a)) {
+ if ((b != undefined) && (b % Int32x4Array.BYTES_PER_ELEMENT) != 0) {
+ throw "byteOffset must be a multiple of 16.";
+ }
+ if (c != undefined) {
+ c *= 4;
+ this.storage_ = new Int32Array(a, b, c);
+ }
+ else {
+ // Note = new Int32Array(a, b) is NOT equivalent to new Float32Array(a, b, undefined)
+ this.storage_ = new Int32Array(a, b);
+ }
+ this.length_ = this.storage_.length / 4;
+ this.byteOffset_ = b != undefined ? b : 0;
+ } else {
+ throw "Unknown type of first argument.";
+ }
+}
+
+Object.defineProperty(Int32x4Array.prototype, 'length',
+ { get: function() { return this.length_; }
+});
+
+Object.defineProperty(Int32x4Array.prototype, 'byteLength',
+ { get: function() { return this.length_ * Int32x4Array.BYTES_PER_ELEMENT; }
+});
+
+Object.defineProperty(Int32x4Array, 'BYTES_PER_ELEMENT',
+ { get: function() { return 16; }
+});
+
+Object.defineProperty(Int32x4Array.prototype, 'BYTES_PER_ELEMENT',
+ { get: function() { return 16; }
+});
+
+Object.defineProperty(Int32x4Array.prototype, 'byteOffset',
+ { get: function() { return this.byteOffset_; }
+});
+
+Object.defineProperty(Int32x4Array.prototype, 'buffer',
+ { get: function() { return this.storage_.buffer; }
+});
+
+Int32x4Array.prototype.getAt = function(i) {
+ if (i < 0) {
+ throw "Index must be >= 0.";
+ }
+ if (i >= this.length) {
+ throw "Index out of bounds.";
+ }
+ var x = this.storage_[i*4+0];
+ var y = this.storage_[i*4+1];
+ var z = this.storage_[i*4+2];
+ var w = this.storage_[i*4+3];
+ return SIMD.int32x4(x, y, z, w);
+}
+
+Int32x4Array.prototype.setAt = function(i, v) {
+ if (i < 0) {
+ throw "Index must be >= 0.";
+ }
+ if (i >= this.length) {
+ throw "Index out of bounds.";
+ }
+ if (!(v instanceof SIMD.int32x4)) {
+ throw "Value is not a int32x4.";
+ }
+ this.storage_[i*4+0] = v.x;
+ this.storage_[i*4+1] = v.y;
+ this.storage_[i*4+2] = v.z;
+ this.storage_[i*4+3] = v.w;
+} \ No newline at end of file
diff --git a/src/struct_info.json b/src/struct_info.json
index f762bf2b..54c89fd7 100644
--- a/src/struct_info.json
+++ b/src/struct_info.json
@@ -932,6 +932,16 @@
"file": "SDL/SDL_events.h",
"defines": [],
"structs": {
+ "SDL_WindowEvent": [
+ "type",
+ "windowID",
+ "event",
+ "padding1",
+ "padding2",
+ "padding3",
+ "data1",
+ "data2"
+ ],
"SDL_KeyboardEvent": [
"type",
"windowID",
@@ -969,6 +979,14 @@
"x",
"y"
],
+ "SDL_MouseWheelEvent": [
+ "type",
+ "timestamp",
+ "windowID",
+ "which",
+ "x",
+ "y"
+ ],
"SDL_JoyAxisEvent": [
"type",
"which",