summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc2
-rwxr-xr-xemscripten.py12
-rw-r--r--src/analyzer.js16
-rwxr-xr-xsrc/embind/embind.js631
-rwxr-xr-xsrc/embind/emval.js60
-rw-r--r--src/jsifier.js60
-rw-r--r--src/library.js63
-rw-r--r--src/library_browser.js6
-rw-r--r--src/library_gl.js8
-rw-r--r--src/preamble.js2
-rw-r--r--src/settings.js1
-rwxr-xr-xsystem/include/emscripten/bind.h192
-rw-r--r--system/include/emscripten/val.h57
-rwxr-xr-xsystem/include/emscripten/wire.h34
-rwxr-xr-xsystem/lib/embind/bind.cpp23
-rw-r--r--tests/cases/legalizer_ta2.ll1
-rw-r--r--tests/cases/longjmp_tiny.ll (renamed from tests/cases/longjmp_tiny_noasm.ll)0
-rw-r--r--tests/cases/longjmp_tiny.txt (renamed from tests/cases/longjmp_tiny_noasm.txt)0
-rw-r--r--tests/cases/longjmp_tiny_invoke.ll (renamed from tests/cases/longjmp_tiny_noasm_invoke.ll)0
-rw-r--r--tests/cases/longjmp_tiny_invoke.txt (renamed from tests/cases/longjmp_tiny_noasm_invoke.txt)0
-rw-r--r--tests/cases/longjmp_tiny_phi.ll (renamed from tests/cases/longjmp_tiny_phi_noasm.ll)0
-rw-r--r--tests/cases/longjmp_tiny_phi.txt (renamed from tests/cases/longjmp_tiny_phi_noasm.txt)0
-rw-r--r--tests/cases/longjmp_tiny_phi2.ll (renamed from tests/cases/longjmp_tiny_phi2_noasm.ll)0
-rw-r--r--tests/cases/longjmp_tiny_phi2.txt (renamed from tests/cases/longjmp_tiny_phi2_noasm.txt)0
-rw-r--r--tests/embind/embind.benchmark.js201
-rwxr-xr-xtests/embind/embind.test.js164
-rw-r--r--tests/embind/embind_benchmark.cpp344
-rw-r--r--tests/embind/embind_test.cpp124
-rw-r--r--tests/embind/shell.html94
-rw-r--r--tests/gl_stride.c152
-rw-r--r--tests/gl_stride.pngbin0 -> 345620 bytes
-rwxr-xr-xtests/runner.py24
-rw-r--r--tools/eliminator/asm-eliminator-test-output.js15
-rw-r--r--tools/eliminator/asm-eliminator-test.js15
-rw-r--r--tools/js-optimizer.js13
-rw-r--r--tools/shared.py4
36 files changed, 1879 insertions, 439 deletions
diff --git a/emcc b/emcc
index 7edc71e4..e25d9d91 100755
--- a/emcc
+++ b/emcc
@@ -1002,7 +1002,7 @@ try:
# Apply effects from settings
if shared.Settings.ASM_JS:
- assert opt_level == 2, 'asm.js requires -O2'
+ assert opt_level >= 1, 'asm.js requires -O1 or above'
if closure:
print >> sys.stderr, 'emcc: warning: disabling closure because it is not compatible with asm.js code generation'
diff --git a/emscripten.py b/emscripten.py
index 6e5f1e7c..6d384a96 100755
--- a/emscripten.py
+++ b/emscripten.py
@@ -426,7 +426,7 @@ function invoke_%s(%s) {
try {
%sModule.dynCall_%s(%s);
} catch(e) {
- asm.setThrew(1);
+ asm.setThrew(1, 0);
}
}
''' % (sig, args, 'return ' if sig[0] != 'v' else '', sig, args)
@@ -489,6 +489,8 @@ var asm = (function(global, env, buffer) {
var HEAPF64 = new global.Float64Array(buffer);
''' % (asm_setup,) + '\n' + asm_global_vars + '''
var __THREW__ = 0;
+ var threwValue = 0;
+ var setjmpId = 0;
var undef = 0;
var tempInt = 0, tempBigInt = 0, tempBigIntP = 0, tempBigIntS = 0, tempBigIntR = 0.0, tempBigIntI = 0, tempBigIntD = 0, tempValue = 0, tempDouble = 0.0;
''' + ''.join(['''
@@ -509,9 +511,13 @@ var asm = (function(global, env, buffer) {
top = top|0;
STACKTOP = top;
}
- function setThrew(threw) {
+ function setThrew(threw, value) {
threw = threw|0;
- __THREW__ = threw;
+ value = value|0;
+ if ((__THREW__|0) == 0) {
+ __THREW__ = threw;
+ threwValue = value;
+ }
}
''' + ''.join(['''
function setTempRet%d(value) {
diff --git a/src/analyzer.js b/src/analyzer.js
index df5a435e..3278139b 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1389,21 +1389,21 @@ function analyzer(data, sidePass) {
var line = label.lines[j];
if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
- var oldIdent = label.ident;
- var newIdent = func.labelIdCounter++;
+ var oldLabel = label.ident;
+ var newLabel = func.labelIdCounter++;
if (!func.setjmpTable) func.setjmpTable = [];
- func.setjmpTable.push([oldIdent, newIdent, line.assignTo]);
+ func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo });
func.labels.splice(i+1, 0, {
intertype: 'label',
- ident: newIdent,
+ ident: newLabel,
lineNum: label.lineNum + 0.5,
lines: label.lines.slice(j+1)
});
- func.labelsDict[newIdent] = func.labels[i+1];
+ func.labelsDict[newLabel] = func.labels[i+1];
label.lines = label.lines.slice(0, j+1);
label.lines.push({
intertype: 'branch',
- label: toNiceIdent(newIdent),
+ label: toNiceIdent(newLabel),
lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
});
// Correct phis
@@ -1412,8 +1412,8 @@ function analyzer(data, sidePass) {
if (phi.intertype == 'phi') {
for (var i = 0; i < phi.params.length; i++) {
var sourceLabelId = getActualLabelId(phi.params[i].label);
- if (sourceLabelId == oldIdent) {
- phi.params[i].label = newIdent;
+ if (sourceLabelId == oldLabel) {
+ phi.params[i].label = newLabel;
}
}
}
diff --git a/src/embind/embind.js b/src/embind/embind.js
index ee717f4b..988526b4 100755
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -1,10 +1,10 @@
/*global Module*/
/*global _malloc, _free, _memcpy*/
-/*global FUNCTION_TABLE, HEAP32, HEAPU8*/
-/*global Pointer_stringify*/
+/*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32*/
+/*global readLatin1String*/
/*global __emval_register, _emval_handle_array, __emval_decref*/
/*global ___getTypeName*/
-
+/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */
var InternalError = Module.InternalError = extendError(Error, 'InternalError');
var BindingError = Module.BindingError = extendError(Error, 'BindingError');
var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError');
@@ -223,9 +223,26 @@ function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverter
}
}
+var __charCodes = (function() {
+ var codes = new Array(256);
+ for (var i = 0; i < 256; ++i) {
+ codes[i] = String.fromCharCode(i);
+ }
+ return codes;
+})();
+
+function readLatin1String(ptr) {
+ var ret = "";
+ var c = ptr;
+ while (HEAPU8[c]) {
+ ret += __charCodes[HEAPU8[c++]];
+ }
+ return ret;
+}
+
function getTypeName(type) {
var ptr = ___getTypeName(type);
- var rv = Pointer_stringify(ptr);
+ var rv = readLatin1String(ptr);
_free(ptr);
return rv;
}
@@ -247,34 +264,35 @@ function requireRegisteredType(rawType, humanName) {
}
function __embind_register_void(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function() {
+ 'fromWireType': function() {
return undefined;
},
});
}
function __embind_register_bool(rawType, name, trueValue, falseValue) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(wt) {
+ 'fromWireType': function(wt) {
// ambiguous emscripten ABI: sometimes return values are
// true or false, and sometimes integers (0 or 1)
return !!wt;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
return o ? trueValue : falseValue;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
// When converting a number from JS to C++ side, the valid range of the number is
// [minRange, maxRange], inclusive.
function __embind_register_integer(primitiveType, name, minRange, maxRange) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
if (maxRange === -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32.
maxRange = 4294967295;
}
@@ -282,10 +300,10 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
name: name,
minRange: minRange,
maxRange: maxRange,
- fromWireType: function(value) {
+ 'fromWireType': function(value) {
return value;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following two if()s and assume value is of proper type.
if (typeof value !== "number") {
@@ -296,17 +314,18 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
}
return value | 0;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
function __embind_register_float(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(value) {
+ 'fromWireType': function(value) {
return value;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following if() and assume value is of proper type.
if (typeof value !== "number") {
@@ -314,15 +333,16 @@ function __embind_register_float(rawType, name) {
}
return value;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
-function __embind_register_cstring(rawType, name) {
- name = Pointer_stringify(name);
+function __embind_register_std_string(rawType, name) {
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(value) {
- var length = HEAP32[value >> 2];
+ 'fromWireType': function(value) {
+ var length = HEAPU32[value >> 2];
var a = new Array(length);
for (var i = 0; i < length; ++i) {
a[i] = String.fromCharCode(HEAPU8[value + 4 + i]);
@@ -330,32 +350,102 @@ function __embind_register_cstring(rawType, name) {
_free(value);
return a.join('');
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
+ if (value instanceof ArrayBuffer) {
+ value = new Uint8Array(value);
+ }
+
+ function getTAElement(ta, index) {
+ return ta[index];
+ }
+ function getStringElement(string, index) {
+ return string.charCodeAt(index);
+ }
+ var getElement;
+ if (value instanceof Uint8Array) {
+ getElement = getTAElement;
+ } else if (value instanceof Int8Array) {
+ getElement = getTAElement;
+ } else if (typeof value === 'string') {
+ getElement = getStringElement;
+ } else {
+ throwBindingError('Cannot pass non-string to std::string');
+ }
+
// assumes 4-byte alignment
var length = value.length;
var ptr = _malloc(4 + length);
- HEAP32[ptr >> 2] = length;
+ HEAPU32[ptr >> 2] = length;
+ for (var i = 0; i < length; ++i) {
+ var charCode = getElement(value, i);
+ if (charCode > 255) {
+ _free(ptr);
+ throwBindingError('String has UTF-16 code units that do not fit in 8 bits');
+ }
+ HEAPU8[ptr + 4 + i] = charCode;
+ }
+ if (destructors !== null) {
+ destructors.push(_free, ptr);
+ }
+ return ptr;
+ },
+ destructorFunction: function(ptr) { _free(ptr); },
+ });
+}
+
+function __embind_register_std_wstring(rawType, charSize, name) {
+ name = readLatin1String(name);
+ var HEAP, shift;
+ if (charSize === 2) {
+ HEAP = HEAPU16;
+ shift = 1;
+ } else if (charSize === 4) {
+ HEAP = HEAPU32;
+ shift = 2;
+ }
+ registerType(rawType, {
+ name: name,
+ 'fromWireType': function(value) {
+ var length = HEAPU32[value >> 2];
+ var a = new Array(length);
+ var start = (value + 4) >> shift;
+ for (var i = 0; i < length; ++i) {
+ a[i] = String.fromCharCode(HEAP[start + i]);
+ }
+ _free(value);
+ return a.join('');
+ },
+ 'toWireType': function(destructors, value) {
+ // assumes 4-byte alignment
+ var length = value.length;
+ var ptr = _malloc(4 + length * charSize);
+ HEAPU32[ptr >> 2] = length;
+ var start = (ptr + 4) >> shift;
for (var i = 0; i < length; ++i) {
- HEAPU8[ptr + 4 + i] = value.charCodeAt(i);
+ HEAP[start + i] = value.charCodeAt(i);
+ }
+ if (destructors !== null) {
+ destructors.push(_free, ptr);
}
- destructors.push(_free, ptr);
return ptr;
},
+ destructorFunction: function(ptr) { _free(ptr); },
});
}
function __embind_register_emval(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(handle) {
+ 'fromWireType': function(handle) {
var rv = _emval_handle_array[handle].value;
__emval_decref(handle);
return rv;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
return __emval_register(value);
},
+ destructorFunction: null, // This type does not need a destructor
});
}
@@ -367,30 +457,142 @@ function runDestructors(destructors) {
}
}
-function makeInvoker(name, argCount, argTypes, invoker, fn) {
- if (!FUNCTION_TABLE[fn]) {
- throwBindingError('function '+name+' is not defined');
+// Function implementation of operator new, per
+// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
+// 13.2.2
+// ES3
+function new_(constructor, argumentList) {
+ if (!(constructor instanceof Function)) {
+ throw new TypeError('new_ called with constructor type ' + typeof(constructor) + " which is not a function");
+ }
+
+ /*
+ * Previously, the following line was just:
+
+ function dummy() {};
+
+ * Unfortunately, Chrome was preserving 'dummy' as the object's name, even though at creation, the 'dummy' has the
+ * correct constructor name. Thus, objects created with IMVU.new would show up in the debugger as 'dummy', which
+ * isn't very helpful. Using IMVU.createNamedFunction addresses the issue. Doublely-unfortunately, there's no way
+ * to write a test for this behavior. -NRD 2013.02.22
+ */
+ var dummy = createNamedFunction(constructor.name, function(){});
+ dummy.prototype = constructor.prototype;
+ var obj = new dummy;
+
+ var r = constructor.apply(obj, argumentList);
+ return (r instanceof Object) ? r : obj;
+}
+
+// The path to interop from JS code to C++ code:
+// (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function)
+// craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind.
+function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) {
+ // humanName: a human-readable string name for the function to be generated.
+ // argTypes: An array that contains the embind type objects for all types in the function signature.
+ // argTypes[0] is the type object for the function return value.
+ // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method.
+ // argTypes[2...] are the actual function parameters.
+ // classType: The embind type object for the class to be bound, or null if this is not a method of a class.
+ // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code.
+ // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling.
+ var argCount = argTypes.length;
+
+ if (argCount < 2) {
+ throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!");
+ }
+
+ 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) {
+// return FUNCTION_TABLE[fn];
+// }
+
+ var argsList = "";
+ var argsListWired = "";
+ for(var i = 0; i < argCount-2; ++i) {
+ argsList += (i!==0?", ":"")+"arg"+i;
+ argsListWired += (i!==0?", ":"")+"arg"+i+"Wired";
}
- return createNamedFunction(makeLegalFunctionName(name), function() {
- if (arguments.length !== argCount - 1) {
- throwBindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1));
+
+ var invokerFnBody =
+ "return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n" +
+ "if (arguments.length !== "+(argCount - 2)+") {\n" +
+ "throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount - 2)+" args!');\n" +
+ "}\n";
+
+ // Determine if we need to use a dynamic stack to store the destructors for the function parameters.
+ // TODO: Remove this completely once all function invokers are being dynamically generated.
+ var needsDestructorStack = false;
+
+ for(var i = 1; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here.
+ if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { // The type does not define a destructor function - must use dynamic stack
+ needsDestructorStack = true;
+ break;
}
- var destructors = [];
- var args = new Array(argCount);
- args[0] = fn;
- for (var i = 1; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
+ }
+
+ if (needsDestructorStack) {
+ invokerFnBody +=
+ "var destructors = [];\n";
+ }
+
+ 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]];
+
+ if (isClassMethodFunc) {
+ invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n";
+ }
+
+ 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]);
+ }
+
+ if (isClassMethodFunc) {
+ argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired;
+ }
+
+ var returns = (argTypes[0].name !== "void");
+
+ invokerFnBody +=
+ (returns?"var rv = ":"") + "invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";
+
+ if (needsDestructorStack) {
+ 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"));
+ if (argTypes[i].destructorFunction !== null) {
+ invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";
+ args1.push(paramName+"_dtor");
+ args2.push(argTypes[i].destructorFunction);
+ }
}
- var rv = invoker.apply(null, args);
- rv = argTypes[0].fromWireType(rv);
- runDestructors(destructors);
- return rv;
- });
+ }
+
+ if (returns) {
+ invokerFnBody += "return retType.fromWireType(rv);\n";
+ }
+ invokerFnBody += "}\n";
+
+ args1.push(invokerFnBody);
+
+ var invokerFunction = new_(Function, args1).apply(null, args2);
+ return invokerFunction;
}
function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) {
var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
rawInvoker = FUNCTION_TABLE[rawInvoker];
exposePublicSymbol(name, function() {
@@ -398,7 +600,8 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
}, argCount - 1);
whenDependentTypesAreResolved([], argTypes, function(argTypes) {
- replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn), argCount - 1);
+ var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
+ replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn), argCount - 1);
return [];
});
}
@@ -407,7 +610,7 @@ var tupleRegistrations = {};
function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) {
tupleRegistrations[rawType] = {
- name: Pointer_stringify(name),
+ name: readLatin1String(name),
rawConstructor: FUNCTION_TABLE[rawConstructor],
rawDestructor: FUNCTION_TABLE[rawDestructor],
elements: [],
@@ -453,18 +656,18 @@ function __embind_finalize_tuple(rawTupleType) {
var setter = elt.setter;
var setterContext = elt.setterContext;
elt.read = function(ptr) {
- return getterReturnType.fromWireType(getter(getterContext, ptr));
+ return getterReturnType['fromWireType'](getter(getterContext, ptr));
};
elt.write = function(ptr, o) {
var destructors = [];
- setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o));
+ setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o));
runDestructors(destructors);
};
});
return [{
name: reg.name,
- fromWireType: function(ptr) {
+ 'fromWireType': function(ptr) {
var rv = new Array(elementsLength);
for (var i = 0; i < elementsLength; ++i) {
rv[i] = elements[i].read(ptr);
@@ -472,7 +675,7 @@ function __embind_finalize_tuple(rawTupleType) {
rawDestructor(ptr);
return rv;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
if (elementsLength !== o.length) {
throw new TypeError("Incorrect number of tuple elements");
}
@@ -480,9 +683,12 @@ function __embind_finalize_tuple(rawTupleType) {
for (var i = 0; i < elementsLength; ++i) {
elements[i].write(ptr, o[i]);
}
- destructors.push(rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(rawDestructor, ptr);
+ }
return ptr;
},
+ destructorFunction: rawDestructor,
}];
});
}
@@ -496,7 +702,7 @@ function __embind_register_struct(
rawDestructor
) {
structRegistrations[rawType] = {
- name: Pointer_stringify(name),
+ name: readLatin1String(name),
rawConstructor: FUNCTION_TABLE[rawConstructor],
rawDestructor: FUNCTION_TABLE[rawDestructor],
fields: [],
@@ -514,7 +720,7 @@ function __embind_register_struct_field(
setterContext
) {
structRegistrations[structType].fields.push({
- fieldName: Pointer_stringify(fieldName),
+ fieldName: readLatin1String(fieldName),
getterReturnType: getterReturnType,
getter: FUNCTION_TABLE[getter],
getterContext: getterContext,
@@ -545,12 +751,12 @@ function __embind_finalize_struct(structType) {
var setterContext = field.setterContext;
fields[fieldName] = {
read: function(ptr) {
- return getterReturnType.fromWireType(
+ return getterReturnType['fromWireType'](
getter(getterContext, ptr));
},
write: function(ptr, o) {
var destructors = [];
- setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o));
+ setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o));
runDestructors(destructors);
}
};
@@ -558,7 +764,7 @@ function __embind_finalize_struct(structType) {
return [{
name: reg.name,
- fromWireType: function(ptr) {
+ 'fromWireType': function(ptr) {
var rv = {};
for (var i in fields) {
rv[i] = fields[i].read(ptr);
@@ -566,7 +772,7 @@ function __embind_finalize_struct(structType) {
rawDestructor(ptr);
return rv;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations:
// assume all fields are present without checking.
for (var fieldName in fields) {
@@ -578,55 +784,17 @@ function __embind_finalize_struct(structType) {
for (fieldName in fields) {
fields[fieldName].write(ptr, o[fieldName]);
}
- destructors.push(rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(rawDestructor, ptr);
+ }
return ptr;
},
+ destructorFunction: rawDestructor,
}];
});
}
-function RegisteredPointer(
- name,
- registeredClass,
- isReference,
- isConst,
-
- // smart pointer properties
- isSmartPointer,
- pointeeType,
- sharingPolicy,
- rawGetPointee,
- rawConstructor,
- rawShare,
- rawDestructor
-) {
- this.name = name;
- this.registeredClass = registeredClass;
- this.isReference = isReference;
- this.isConst = isConst;
-
- // smart pointer properties
- this.isSmartPointer = isSmartPointer;
- this.pointeeType = pointeeType;
- this.sharingPolicy = sharingPolicy;
- this.rawGetPointee = rawGetPointee;
- this.rawConstructor = rawConstructor;
- this.rawShare = rawShare;
- this.rawDestructor = rawDestructor;
-}
-
-RegisteredPointer.prototype.toWireType = function(destructors, handle) {
- var self = this;
- function throwCannotConvert() {
- var name;
- if (handle.$$.smartPtrType) {
- name = handle.$$.smartPtrType.name;
- } else {
- name = handle.$$.ptrType.name;
- }
- throwBindingError('Cannot convert argument of type ' + name + ' to parameter type ' + self.name);
- }
-
+var genericPointerToWireType = function(destructors, handle) {
if (handle === null) {
if (this.isReference) {
throwBindingError('null is not a valid ' + this.name);
@@ -640,33 +808,39 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) {
return 0;
}
}
- if (!(handle instanceof this.registeredClass.constructor)) {
- throwBindingError('Expected null or instance of ' + this.name + ', got ' + _embind_repr(handle));
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
}
- // TODO: this is not strictly true
- // We could support BY_EMVAL conversions from raw pointers to smart pointers
- // because the smart pointer can hold a reference to the handle
- if (this.isSmartPointer && undefined === handle.$$.smartPtr) {
- throwBindingError('Passing raw pointer to smart pointer is illegal');
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
}
if (!this.isConst && handle.$$.ptrType.isConst) {
- throwCannotConvert();
+ throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name);
}
var handleClass = handle.$$.ptrType.registeredClass;
var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+
if (this.isSmartPointer) {
+ // TODO: this is not strictly true
+ // We could support BY_EMVAL conversions from raw pointers to smart pointers
+ // because the smart pointer can hold a reference to the handle
+ if (undefined === handle.$$.smartPtr) {
+ throwBindingError('Passing raw pointer to smart pointer is illegal');
+ }
+
switch (this.sharingPolicy) {
case 0: // NONE
// no upcasting
if (handle.$$.smartPtrType === this) {
ptr = handle.$$.smartPtr;
} else {
- throwCannotConvert();
+ throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name);
}
break;
case 1: // INTRUSIVE
- throwBindingError('INTRUSIVE sharing policy not yet supported');
+ ptr = handle.$$.smartPtr;
break;
case 2: // BY_EMVAL
@@ -691,6 +865,97 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) {
return ptr;
};
+// If we know a pointer type is not going to have SmartPtr logic in it, we can
+// special-case optimize it a bit (compare to genericPointerToWireType)
+var constNoSmartPtrRawPointerToWireType = function(destructors, handle) {
+ if (handle === null) {
+ if (this.isReference) {
+ throwBindingError('null is not a valid ' + this.name);
+ }
+ return 0;
+ }
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
+ }
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
+ }
+ var handleClass = handle.$$.ptrType.registeredClass;
+ var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+ return ptr;
+};
+
+// An optimized version for non-const method accesses - there we must additionally restrict that
+// the pointer is not a const-pointer.
+var nonConstNoSmartPtrRawPointerToWireType = function(destructors, handle) {
+ if (handle === null) {
+ if (this.isReference) {
+ throwBindingError('null is not a valid ' + this.name);
+ }
+ return 0;
+ }
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
+ }
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
+ }
+ if (handle.$$.ptrType.isConst) {
+ throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + this.name);
+ }
+ var handleClass = handle.$$.ptrType.registeredClass;
+ var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+ return ptr;
+};
+
+function RegisteredPointer(
+ name,
+ registeredClass,
+ isReference,
+ isConst,
+
+ // smart pointer properties
+ isSmartPointer,
+ pointeeType,
+ sharingPolicy,
+ rawGetPointee,
+ rawConstructor,
+ rawShare,
+ rawDestructor
+) {
+ this.name = name;
+ this.registeredClass = registeredClass;
+ this.isReference = isReference;
+ this.isConst = isConst;
+
+ // smart pointer properties
+ this.isSmartPointer = isSmartPointer;
+ this.pointeeType = pointeeType;
+ this.sharingPolicy = sharingPolicy;
+ this.rawGetPointee = rawGetPointee;
+ this.rawConstructor = rawConstructor;
+ this.rawShare = rawShare;
+ this.rawDestructor = rawDestructor;
+
+ if (!isSmartPointer && registeredClass.baseClass === undefined) {
+ if (isConst) {
+ this['toWireType'] = constNoSmartPtrRawPointerToWireType;
+ this.destructorFunction = null;
+ } else {
+ this['toWireType'] = nonConstNoSmartPtrRawPointerToWireType;
+ this.destructorFunction = null;
+ }
+ } else {
+ this['toWireType'] = genericPointerToWireType;
+ // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns
+ // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time.
+ // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in
+ // craftInvokerFunction altogether.
+ }
+}
+
RegisteredPointer.prototype.getPointee = function(ptr) {
if (this.rawGetPointee) {
ptr = this.rawGetPointee(ptr);
@@ -704,7 +969,7 @@ RegisteredPointer.prototype.destructor = function(ptr) {
}
};
-RegisteredPointer.prototype.fromWireType = function(ptr) {
+RegisteredPointer.prototype['fromWireType'] = function(ptr) {
// ptr is a raw pointer (or a raw smartpointer)
// rawPointer is a maybe-null raw pointer
@@ -775,7 +1040,7 @@ function makeClassHandle(prototype, record) {
}
record.count = { value: 1 };
return Object.create(prototype, {
- '$$': {
+ $$: {
value: record,
},
});
@@ -795,7 +1060,7 @@ ClassHandle.prototype.clone = function() {
}
var clone = Object.create(Object.getPrototypeOf(this), {
- '$$': {
+ $$: {
value: shallowCopy(this.$$),
}
});
@@ -865,7 +1130,7 @@ function __embind_register_class(
name,
rawDestructor
) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
rawDestructor = FUNCTION_TABLE[rawDestructor];
getActualType = FUNCTION_TABLE[getActualType];
upcast = FUNCTION_TABLE[upcast];
@@ -988,13 +1253,13 @@ function __embind_register_class_constructor(
var args = new Array(argCount);
args[0] = rawConstructor;
for (var i = 1; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
+ args[i] = argTypes[i]['toWireType'](destructors, arguments[i - 1]);
}
var ptr = invoker.apply(null, args);
runDestructors(destructors);
- return argTypes[0].fromWireType(ptr);
+ return argTypes[0]['fromWireType'](ptr);
};
return [];
});
@@ -1016,6 +1281,9 @@ function downcastPointer(ptr, ptrClass, desiredClass) {
function upcastPointer(ptr, ptrClass, desiredClass) {
while (ptrClass !== desiredClass) {
+ if (!ptrClass.upcast) {
+ throwBindingError("Expected null or instance of " + desiredClass.name + ", got an instance of " + ptrClass.name);
+ }
ptr = ptrClass.upcast(ptr);
ptrClass = ptrClass.baseClass;
}
@@ -1049,7 +1317,7 @@ function __embind_register_class_function(
context
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- methodName = Pointer_stringify(methodName);
+ methodName = readLatin1String(methodName);
rawInvoker = FUNCTION_TABLE[rawInvoker];
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
@@ -1057,8 +1325,8 @@ function __embind_register_class_function(
var humanName = classType.name + '.' + methodName;
var unboundTypesHandler = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ };
var proto = classType.registeredClass.instancePrototype;
var method = proto[methodName];
@@ -1074,25 +1342,8 @@ function __embind_register_class_function(
}
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- var memberFunction = createNamedFunction(makeLegalFunctionName(humanName), function() {
- if (arguments.length !== argCount - 2) {
- throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-2));
- }
-
- validateThis(this, classType, humanName);
-
- var destructors = [];
- var args = new Array(argCount + 1);
- args[0] = context;
- args[1] = argTypes[1].toWireType(destructors, this);
- for (var i = 2; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 2]);
- }
- var rv = rawInvoker.apply(null, args);
- rv = argTypes[0].fromWireType(rv);
- runDestructors(destructors);
- return rv;
- });
+
+ var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context);
// Replace the initial unbound-handler-stub function with the appropriate member function, now that all types
// are resolved. If multiple overloads are registered for this function, the function goes into an overload table.
@@ -1117,7 +1368,7 @@ function __embind_register_class_class_function(
fn
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- methodName = Pointer_stringify(methodName);
+ methodName = readLatin1String(methodName);
rawInvoker = FUNCTION_TABLE[rawInvoker];
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
classType = classType[0];
@@ -1141,7 +1392,8 @@ function __embind_register_class_class_function(
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 func = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
+ 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 {
@@ -1154,46 +1406,65 @@ function __embind_register_class_class_function(
}
function __embind_register_class_property(
- rawClassType,
+ classType,
fieldName,
- rawFieldType,
+ getterReturnType,
getter,
+ getterContext,
+ setterArgumentType,
setter,
- context
+ setterContext
) {
- fieldName = Pointer_stringify(fieldName);
+ fieldName = readLatin1String(fieldName);
getter = FUNCTION_TABLE[getter];
- setter = FUNCTION_TABLE[setter];
- whenDependentTypesAreResolved([], [rawClassType], function(classType) {
+
+ whenDependentTypesAreResolved([], [classType], function(classType) {
classType = classType[0];
var humanName = classType.name + '.' + fieldName;
-
- Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
+ var desc = {
get: function() {
- throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
- },
- set: function() {
- throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
+ throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]);
},
enumerable: true,
configurable: true
- });
+ };
+ if (setter) {
+ desc.set = function() {
+ throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]);
+ };
+ } else {
+ desc.set = function(v) {
+ throwBindingError(humanName + ' is a read-only property');
+ };
+ }
+
+ Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc);
- whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) {
- fieldType = fieldType[0];
- Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
+ whenDependentTypesAreResolved(
+ [],
+ (setter ? [getterReturnType, setterArgumentType] : [getterReturnType]),
+ function(types) {
+ var getterReturnType = types[0];
+ var desc = {
get: function() {
var ptr = validateThis(this, classType, humanName + ' getter');
- return fieldType.fromWireType(getter(context, ptr));
+ return getterReturnType['fromWireType'](getter(getterContext, ptr));
},
- set: function(v) {
+ enumerable: true
+ };
+
+ if (setter) {
+ setter = FUNCTION_TABLE[setter];
+ var setterArgumentType = types[1];
+ desc.set = function(v) {
var ptr = validateThis(this, classType, humanName + ' setter');
var destructors = [];
- setter(context, ptr, fieldType.toWireType(destructors, v));
+ setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, v));
runDestructors(destructors);
- },
- enumerable: true
- });
+ };
+ }
+
+ Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, desc);
return [];
});
@@ -1223,7 +1494,7 @@ function __embind_register_smart_ptr(
rawShare,
rawDestructor
) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
rawGetPointee = FUNCTION_TABLE[rawGetPointee];
rawConstructor = FUNCTION_TABLE[rawConstructor];
rawShare = FUNCTION_TABLE[rawShare];
@@ -1253,7 +1524,7 @@ function __embind_register_enum(
rawType,
name
) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
function constructor() {
}
@@ -1262,12 +1533,13 @@ function __embind_register_enum(
registerType(rawType, {
name: name,
constructor: constructor,
- fromWireType: function(c) {
+ 'fromWireType': function(c) {
return this.constructor.values[c];
},
- toWireType: function(destructors, c) {
+ 'toWireType': function(destructors, c) {
return c.value;
},
+ destructorFunction: null,
});
exposePublicSymbol(name, constructor);
}
@@ -1278,7 +1550,7 @@ function __embind_register_enum_value(
enumValue
) {
var enumType = requireRegisteredType(rawEnumType, 'enum');
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
var Enum = enumType.constructor;
@@ -1290,34 +1562,11 @@ function __embind_register_enum_value(
Enum[name] = Value;
}
-function __embind_register_interface(
- rawType,
- name,
- rawConstructor,
- rawDestructor
-) {
- name = Pointer_stringify(name);
- rawConstructor = FUNCTION_TABLE[rawConstructor];
- rawDestructor = FUNCTION_TABLE[rawDestructor];
-
- registerType(rawType, {
- name: name,
- rawConstructor: rawConstructor,
- rawDestructor: rawDestructor,
- toWireType: function(destructors, o) {
- var handle = __emval_register(o);
- var ptr = this.rawConstructor(handle);
- destructors.push(this.rawDestructor, ptr);
- return ptr;
- },
- });
-}
-
function __embind_register_constant(name, type, value) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
whenDependentTypesAreResolved([], [type], function(type) {
type = type[0];
- Module[name] = type.fromWireType(value);
+ Module[name] = type['fromWireType'](value);
return [];
});
}
diff --git a/src/embind/emval.js b/src/embind/emval.js
index 45030b99..c02ffa92 100755
--- a/src/embind/emval.js
+++ b/src/embind/emval.js
@@ -1,9 +1,9 @@
/*global Module*/
/*global HEAP32*/
-/*global Pointer_stringify, writeStringToMemory*/
-/*global requireRegisteredType*/
+/*global readLatin1String, writeStringToMemory*/
+/*global requireRegisteredType, throwBindingError*/
-var _emval_handle_array = [];
+var _emval_handle_array = [{}]; // reserve zero
var _emval_free_list = [];
// Public JS API
@@ -11,7 +11,7 @@ var _emval_free_list = [];
/** @expose */
Module.count_emval_handles = function() {
var count = 0;
- for (var i = 0; i < _emval_handle_array.length; ++i) {
+ for (var i = 1; i < _emval_handle_array.length; ++i) {
if (_emval_handle_array[i] !== undefined) {
++count;
}
@@ -21,7 +21,7 @@ Module.count_emval_handles = function() {
/** @expose */
Module.get_first_emval = function() {
- for (var i = 0; i < _emval_handle_array.length; ++i) {
+ for (var i = 1; i < _emval_handle_array.length; ++i) {
if (_emval_handle_array[i] !== undefined) {
return _emval_handle_array[i];
}
@@ -31,6 +31,27 @@ Module.get_first_emval = function() {
// Private C++ API
+var _emval_symbols = {}; // address -> string
+
+function __emval_register_symbol(address) {
+ _emval_symbols[address] = readLatin1String(address);
+}
+
+function getStringOrSymbol(address) {
+ var symbol = _emval_symbols[address];
+ if (symbol === undefined) {
+ return readLatin1String(address);
+ } else {
+ return symbol;
+ }
+}
+
+function requireHandle(handle) {
+ if (!handle) {
+ throwBindingError('Cannot use deleted val. handle = ' + handle);
+ }
+}
+
function __emval_register(value) {
var handle = _emval_free_list.length ?
_emval_free_list.pop() :
@@ -41,11 +62,13 @@ function __emval_register(value) {
}
function __emval_incref(handle) {
- _emval_handle_array[handle].refcount += 1;
+ if (handle) {
+ _emval_handle_array[handle].refcount += 1;
+ }
}
function __emval_decref(handle) {
- if (0 === --_emval_handle_array[handle].refcount) {
+ if (handle && 0 === --_emval_handle_array[handle].refcount) {
delete _emval_handle_array[handle];
_emval_free_list.push(handle);
@@ -74,7 +97,7 @@ function __emval_null() {
}
function __emval_new_cstring(v) {
- return __emval_register(Pointer_stringify(v));
+ return __emval_register(getStringOrSymbol(v));
}
function __emval_take_value(type, v) {
@@ -86,6 +109,8 @@ function __emval_take_value(type, v) {
var __newers = {}; // arity -> function
function __emval_new(handle, argCount, argTypes) {
+ requireHandle(handle);
+
var args = parseParameters(
argCount,
argTypes,
@@ -127,24 +152,27 @@ function __emval_new(handle, argCount, argTypes) {
var global = (function(){return Function;})()('return this')();
function __emval_get_global(name) {
- name = Pointer_stringify(name);
+ name = getStringOrSymbol(name);
return __emval_register(global[name]);
}
function __emval_get_module_property(name) {
- name = Pointer_stringify(name);
+ name = getStringOrSymbol(name);
return __emval_register(Module[name]);
}
function __emval_get_property(handle, key) {
+ requireHandle(handle);
return __emval_register(_emval_handle_array[handle].value[_emval_handle_array[key].value]);
}
function __emval_set_property(handle, key, value) {
+ requireHandle(handle);
_emval_handle_array[handle].value[_emval_handle_array[key].value] = _emval_handle_array[value].value;
}
function __emval_as(handle, returnType) {
+ requireHandle(handle);
returnType = requireRegisteredType(returnType, 'emval::as');
var destructors = [];
// caller owns destructing
@@ -163,6 +191,7 @@ function parseParameters(argCount, argTypes, argWireTypes) {
}
function __emval_call(handle, argCount, argTypes) {
+ requireHandle(handle);
var fn = _emval_handle_array[handle].value;
var args = parseParameters(
argCount,
@@ -173,7 +202,8 @@ function __emval_call(handle, argCount, argTypes) {
}
function __emval_call_method(handle, name, argCount, argTypes) {
- name = Pointer_stringify(name);
+ requireHandle(handle);
+ name = getStringOrSymbol(name);
var args = parseParameters(
argCount,
@@ -185,7 +215,8 @@ function __emval_call_method(handle, name, argCount, argTypes) {
}
function __emval_call_void_method(handle, name, argCount, argTypes) {
- name = Pointer_stringify(name);
+ requireHandle(handle);
+ name = getStringOrSymbol(name);
var args = parseParameters(
argCount,
@@ -194,3 +225,8 @@ function __emval_call_void_method(handle, name, argCount, argTypes) {
var obj = _emval_handle_array[handle].value;
obj[name].apply(obj, args);
}
+
+function __emval_has_function(handle, name) {
+ name = getStringOrSymbol(name);
+ return _emval_handle_array[handle].value[name] instanceof Function;
+}
diff --git a/src/jsifier.js b/src/jsifier.js
index 926be71a..a01b2655 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -701,18 +701,22 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += indent + 'label = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n';
} // otherwise, should have been set before!
if (func.setjmpTable) {
- assert(!ASM_JS, 'asm.js mode does not support setjmp yet');
- var setjmpTable = {};
- ret += indent + 'var mySetjmpIds = {};\n';
- ret += indent + 'var setjmpTable = {';
- func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
- ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { label = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },';
- });
- ret += 'dummy: 0';
- ret += '};\n';
+ if (!ASM_JS) {
+ var setjmpTable = {};
+ ret += indent + 'var mySetjmpIds = {};\n';
+ ret += indent + 'var setjmpTable = {';
+ func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ ret += '"' + getLabelId(triple.oldLabel) + '": ' + 'function(value) { label = ' + getLabelId(triple.newLabel) + '; ' + triple.assignTo + ' = value },';
+ });
+ ret += 'dummy: 0';
+ ret += '};\n';
+ } else {
+ ret += 'var setjmpLabel = 0;\n';
+ ret += 'var setjmpTable = ' + RuntimeGenerator.stackAlloc(4 * (MAX_SETJMPS + 1) * 2) + ';\n';
+ }
}
ret += indent + 'while(1) ';
- if (func.setjmpTable) {
+ if (func.setjmpTable && !ASM_JS) {
ret += 'try { ';
}
ret += 'switch(' + asmCoercion('label', 'i32') + ') {\n';
@@ -720,9 +724,19 @@ function JSify(data, functionsOnly, givenFunctions) {
return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n'
+ getLabelLines(label, indent + ' ');
}).join('\n') + '\n';
+ if (func.setjmpTable && ASM_JS) {
+ // emit a label in which we write to the proper local variable, before jumping to the actual label
+ ret += ' case -1111: ';
+ ret += func.setjmpTable.map(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into
+ return 'if ((setjmpLabel|0) == ' + getLabelId(triple.oldLabel) + ') { ' + triple.assignTo + ' = threwValue; label = ' + triple.newLabel + ' }\n';
+ }).join(' else ');
+ if (ASSERTIONS) ret += 'else abort(-3);\n';
+ ret += '__THREW__ = threwValue = 0;\n';
+ ret += 'break;\n';
+ }
if (ASSERTIONS) ret += indent + ' default: assert(0' + (ASM_JS ? '' : ', "bad label: " + label') + ');\n';
ret += indent + '}\n';
- if (func.setjmpTable) {
+ if (func.setjmpTable && !ASM_JS) {
ret += ' } catch(e) { if (!e.longjmp || !(e.id in mySetjmpIds)) throw(e); setjmpTable[setjmpLabels[e.id]](e.value) }';
}
} else {
@@ -1186,7 +1200,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
item.reloopingJS = ret; // everything but the actual branching (which the relooper will do for us)
item.toLabelJS = getPhiSetsForLabel(phiSets, item.toLabel);
- item.unwindLabelJS = getPhiSetsForLabel(phiSets, item.unwindLabel);
+ item.unwindLabelJS = (ASM_JS ? '__THREW__ = 0;' : '') + getPhiSetsForLabel(phiSets, item.unwindLabel);
ret += 'if (!__THREW__) { ' + item.toLabelJS + makeBranch(item.toLabel, item.currLabelId)
+ ' } else { ' + item.unwindLabelJS + makeBranch(item.unwindLabel, item.currLabelId) + ' }';
return ret;
@@ -1210,6 +1224,12 @@ function JSify(data, functionsOnly, givenFunctions) {
}
});
makeFuncLineActor('landingpad', function(item) {
+ if (DISABLE_EXCEPTION_CATCHING && USE_TYPED_ARRAYS == 2) {
+ ret = makeVarDef(item.assignTo) + '$0 = 0; ' + item.assignTo + '$1 = 0;';
+ item.assignTo = null;
+ if (ASSERTIONS) warnOnce('landingpad, but exceptions are disabled!');
+ return ret;
+ }
var catchTypeArray = item.catchables.map(finalizeLLVMParameter).map(function(element) { return asmCoercion(element, 'i32') }).join(',');
var ret = asmCoercion('___cxa_find_matching_catch(-1, -1' + (catchTypeArray.length > 0 ? ',' + catchTypeArray : '') +')', 'i32');
if (USE_TYPED_ARRAYS == 2) {
@@ -1295,6 +1315,8 @@ function JSify(data, functionsOnly, givenFunctions) {
// We cannot compile assembly. See comment in intertyper.js:'Call'
assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!');
+ if (ASM_JS && funcData.setjmpTable) forceByPointer = true; // in asm.js mode, we must do an invoke for each call
+
ident = Variables.resolveAliasToIdent(ident);
var shortident = ident.slice(1);
var simpleIdent = shortident;
@@ -1449,6 +1471,13 @@ function JSify(data, functionsOnly, givenFunctions) {
ret += '; return 0'; // special case: abort() can happen without return, breaking the return type of asm functions. ensure a return
}
}
+
+ if (ASM_JS && funcData.setjmpTable) {
+ // check if a longjmp was done. If a setjmp happened, check if ours. If ours, go to -111 to handle it.
+ // otherwise, just return - the call to us must also have been an invoke, so the setjmp propagates that way
+ ret += '; if (((__THREW__|0) != 0) & ((threwValue|0) > 0)) { setjmpLabel = _testSetjmp(' + makeGetValue('__THREW__', 0, 'i32') + ', setjmpTable); if ((setjmpLabel|0) > 0) { label = -1111; break } else return ' + (funcData.returnType != 'void' ? asmCoercion('0', funcData.returnType) : '') + ' } __THREW__ = threwValue = 0;\n';
+ }
+
return ret;
}
makeFuncLineActor('getelementptr', function(item) { return finalizeLLVMFunctionCall(item) });
@@ -1458,11 +1487,12 @@ function JSify(data, functionsOnly, givenFunctions) {
});
makeFuncLineActor('unreachable', function(item) {
+ var ret = '';
+ if (ASM_JS && item.funcData.returnType != 'void') ret = 'return ' + asmCoercion('0', item.funcData.returnType) + ';';
if (ASSERTIONS) {
- return ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"';
- } else {
- return ';';
+ ret = (ASM_JS ? 'abort()' : 'throw "Reached an unreachable!"') + ';' + ret;
}
+ return ret || ';';
});
// Final combiner
diff --git a/src/library.js b/src/library.js
index 11f30a1c..2663512d 100644
--- a/src/library.js
+++ b/src/library.js
@@ -573,7 +573,7 @@ LibraryManager.library = {
eof: false,
ungotten: []
};
- assert(Math.max(_stdin, _stdout, _stderr) < 128); // make sure these are low, we flatten arrays with these
+ assert(Math.max(_stdin, _stdout, _stderr) < 1024); // make sure these are low, we flatten arrays with these
{{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}};
{{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}};
{{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}};
@@ -6217,13 +6217,74 @@ LibraryManager.library = {
// related functionality so the slowdown is more limited.
// ==========================================================================
+ saveSetjmp__asm: true,
+ saveSetjmp__sig: 'iii',
+ saveSetjmp: function(env, label, table) {
+ // Not particularly fast: slow table lookup of setjmpId to label. But setjmp
+ // prevents relooping anyhow, so slowness is to be expected. And typical case
+ // is 1 setjmp per invocation, or less.
+ env = env|0;
+ label = label|0;
+ table = table|0;
+ var i = 0;
+#if ASSERTIONS
+ if ((label|0) == 0) abort(121);
+#endif
+ setjmpId = (setjmpId+1)|0;
+ {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}};
+ while ((i|0) < {{{ MAX_SETJMPS }}}) {
+ if ({{{ makeGetValueAsm('table', 'i*4', 'i32') }}} == 0) {
+ {{{ makeSetValueAsm('table', 'i*4', 'setjmpId', 'i32') }}};
+ {{{ makeSetValueAsm('table', 'i*4+4', 'label', 'i32') }}};
+ // prepare next slot
+ {{{ makeSetValueAsm('table', 'i*4+8', '0', 'i32') }}};
+ return 0;
+ }
+ i = (i+2)|0;
+ }
+ abort(987); // if you hit this, adjust MAX_SETJMPS
+ return 0;
+ },
+
+ testSetjmp__asm: true,
+ testSetjmp__sig: 'iii',
+ testSetjmp: function(id, table) {
+ id = id|0;
+ table = table|0;
+ var i = 0, curr = 0;
+ while ((i|0) < {{{ MAX_SETJMPS }}}) {
+ curr = {{{ makeGetValueAsm('table', 'i*4', 'i32') }}};
+ if ((curr|0) == 0) break;
+ if ((curr|0) == (id|0)) {
+ return {{{ makeGetValueAsm('table', 'i*4+4', 'i32') }}};
+ }
+ i = (i+2)|0;
+ }
+ return 0;
+ },
+
+#if ASM_JS
+ setjmp__deps: ['saveSetjmp', 'testSetjmp'],
+#endif
setjmp__inline: function(env) {
// Save the label
+#if ASM_JS
+ return '_saveSetjmp(' + env + ', label, setjmpTable)';
+#else
return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)';
+#endif
},
+#if ASM_JS
+ longjmp__deps: ['saveSetjmp', 'testSetjmp'],
+#endif
longjmp: function(env, value) {
+#if ASM_JS
+ asm.setThrew(env, value || 1);
+ throw 'longjmp';
+#else
throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
+#endif
},
// ==========================================================================
diff --git a/src/library_browser.js b/src/library_browser.js
index c1add740..85eb93f7 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -704,7 +704,11 @@ mergeInto(LibraryManager.library, {
},
emscripten_get_now: function() {
- if (window['performance'] && window['performance']['now']) {
+ if (ENVIRONMENT_IS_NODE) {
+ var t = process['hrtime']();
+ return t[0] * 1e3 + t[1] / 1e6;
+ }
+ else if (window['performance'] && window['performance']['now']) {
return window['performance']['now']();
} else {
return Date.now();
diff --git a/src/library_gl.js b/src/library_gl.js
index 0912b5da..b91d782c 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -2282,14 +2282,14 @@ var LibraryGL = {
}
attributes.sort(function(x, y) { return !x ? (!y ? 0 : 1) : (!y ? -1 : (x.pointer - y.pointer)) });
start = GL.currArrayBuffer ? 0 : attributes[0].pointer;
+ var multiStrides = false;
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (!attribute) break;
-#if ASSERTIONS
- assert(stride == 0 || stride == attribute.stride); // must all be in the same buffer
-#endif
+ if (stride != 0 && stride != attribute.stride) multiStrides = true;
if (attribute.stride) stride = attribute.stride;
}
+ if (multiStrides) stride = 0; // we will need to restride
var bytes = 0; // total size in bytes
if (!stride && !beginEnd) {
// beginEnd can not have stride in the attributes, that is fine. otherwise,
@@ -2297,7 +2297,7 @@ var LibraryGL = {
// our emulation code simple, we perform unpacking/restriding here. this adds overhead, so
// it is a good idea to not hit this!
#if ASSERTIONS
- Runtime.warnOnce('Unpacking/restriding attributes, this is not fast');
+ Runtime.warnOnce('Unpacking/restriding attributes, this is slow and dangerous');
#endif
if (!GL.immediate.restrideBuffer) GL.immediate.restrideBuffer = _malloc(GL.MAX_TEMP_BUFFER_SIZE);
start = GL.immediate.restrideBuffer;
diff --git a/src/preamble.js b/src/preamble.js
index 592363f9..17b74cd9 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -217,8 +217,10 @@ var START_TIME = Date.now();
//========================================
var __THREW__ = 0; // Used in checking for thrown exceptions.
+#if ASM_JS == 0
var setjmpId = 1; // Used in setjmp/longjmp
var setjmpLabels = {};
+#endif
var ABORT = false;
diff --git a/src/settings.js b/src/settings.js
index 8f31c2d8..db24cca4 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -55,6 +55,7 @@ var ALLOW_MEMORY_GROWTH = 0; // If false, we abort with an error if we try to al
// that case we must be careful about optimizations, in particular the
// eliminator). Note that memory growth is only supported with typed
// arrays.
+var MAX_SETJMPS = 10; // size of setjmp table allocated in each function invocation (that has setjmp)
// Code embetterments
var MICRO_OPTS = 1; // Various micro-optimizations, like nativizing variables
diff --git a/system/include/emscripten/bind.h b/system/include/emscripten/bind.h
index 57e44c0c..4d2f4ac8 100755
--- a/system/include/emscripten/bind.h
+++ b/system/include/emscripten/bind.h
@@ -47,10 +47,15 @@ namespace emscripten {
TYPEID floatType,
const char* name);
- void _embind_register_cstring(
+ void _embind_register_std_string(
TYPEID stringType,
const char* name);
+ void _embind_register_std_wstring(
+ TYPEID stringType,
+ size_t charSize,
+ const char* name);
+
void _embind_register_emval(
TYPEID emvalType,
const char* name);
@@ -136,10 +141,12 @@ namespace emscripten {
void _embind_register_class_property(
TYPEID classType,
const char* fieldName,
- TYPEID fieldType,
+ TYPEID getterReturnType,
GenericFunction getter,
+ void* getterContext,
+ TYPEID setterArgumentType,
GenericFunction setter,
- void* context);
+ void* setterContext);
void _embind_register_class_class_function(
TYPEID classType,
@@ -223,6 +230,18 @@ namespace emscripten {
}
namespace internal {
+ template<typename ClassType, typename Signature>
+ struct MemberFunctionType {
+ typedef Signature (ClassType::*type);
+ };
+ }
+
+ template<typename Signature, typename ClassType>
+ typename internal::MemberFunctionType<ClassType, Signature>::type select_overload(Signature (ClassType::*fn)) {
+ return fn;
+ }
+
+ namespace internal {
template<typename ReturnType, typename... Args>
struct Invoker {
static typename internal::BindingType<ReturnType>::WireType invoke(
@@ -277,8 +296,8 @@ namespace emscripten {
}
template<typename WrapperType, typename ClassType, typename... Args>
- WrapperType wrapped_new(Args... args) {
- return WrapperType(new ClassType(args...));
+ WrapperType wrapped_new(Args&&... args) {
+ return WrapperType(new ClassType(std::forward<Args>(args)...));
}
template<typename ClassType, typename... Args>
@@ -711,28 +730,41 @@ namespace emscripten {
template<typename T>
class wrapper : public T {
public:
- wrapper(const val& wrapped)
- : wrapped(wrapped)
+ explicit wrapper(val&& wrapped)
+ : wrapped(std::forward<val>(wrapped))
{}
template<typename ReturnType, typename... Args>
- ReturnType call(const char* name, Args... args) const {
- return Caller<ReturnType, Args...>::call(wrapped, name, args...);
+ ReturnType call(const char* name, Args&&... args) const {
+ return Caller<ReturnType, Args...>::call(wrapped, name, std::forward<Args>(args)...);
+ }
+
+ template<typename ReturnType, typename... Args, typename Default>
+ ReturnType optional_call(const char* name, Default def, Args&&... args) const {
+ if (has_function(name)) {
+ return Caller<ReturnType, Args...>::call(wrapped, name, std::forward<Args>(args)...);
+ } else {
+ return def();
+ }
}
private:
+ bool has_function(const char* name) const {
+ return wrapped.has_function(name);
+ }
+
// this class only exists because you can't partially specialize function templates
template<typename ReturnType, typename... Args>
struct Caller {
- static ReturnType call(const val& v, const char* name, Args... args) {
- return v.call(name, args...).template as<ReturnType>();
+ static ReturnType call(const val& v, const char* name, Args&&... args) {
+ return v.call(name, std::forward<Args>(args)...).template as<ReturnType>();
}
};
template<typename... Args>
struct Caller<void, Args...> {
- static void call(const val& v, const char* name, Args... args) {
- v.call_void(name, args...);
+ static void call(const val& v, const char* name, Args&&... args) {
+ v.call_void(name, std::forward<Args>(args)...);
}
};
@@ -740,7 +772,7 @@ namespace emscripten {
};
#define EMSCRIPTEN_WRAPPER(T) \
- T(const ::emscripten::val& v): wrapper(v) {}
+ T(::emscripten::val&& v): wrapper(std::forward<::emscripten::val>(v)) {}
namespace internal {
struct NoBaseClass {
@@ -971,7 +1003,23 @@ namespace emscripten {
return *this;
}
- template<typename FieldType>
+ template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type>
+ class_& property(const char* fieldName, const FieldType ClassType::*field) {
+ using namespace internal;
+
+ _embind_register_class_property(
+ TypeID<ClassType>::get(),
+ fieldName,
+ TypeID<FieldType>::get(),
+ reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>),
+ getContext(field),
+ 0,
+ 0,
+ 0);
+ return *this;
+ }
+
+ template<typename FieldType, typename = typename std::enable_if<!std::is_function<FieldType>::value>::type>
class_& property(const char* fieldName, FieldType ClassType::*field) {
using namespace internal;
@@ -980,11 +1028,46 @@ namespace emscripten {
fieldName,
TypeID<FieldType>::get(),
reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template getWire<ClassType>),
+ getContext(field),
+ TypeID<FieldType>::get(),
reinterpret_cast<GenericFunction>(&MemberAccess<ClassType, FieldType>::template setWire<ClassType>),
getContext(field));
return *this;
}
+ template<typename Getter>
+ class_& property(const char* fieldName, Getter getter) {
+ using namespace internal;
+ typedef GetterPolicy<Getter> GP;
+ _embind_register_class_property(
+ TypeID<ClassType>::get(),
+ fieldName,
+ TypeID<typename GP::ReturnType>::get(),
+ reinterpret_cast<GenericFunction>(&GP::template get<ClassType>),
+ GP::getContext(getter),
+ 0,
+ 0,
+ 0);
+ return *this;
+ }
+
+ template<typename Getter, typename Setter>
+ class_& property(const char* fieldName, Getter getter, Setter setter) {
+ using namespace internal;
+ typedef GetterPolicy<Getter> GP;
+ typedef SetterPolicy<Setter> SP;
+ _embind_register_class_property(
+ TypeID<ClassType>::get(),
+ fieldName,
+ TypeID<typename GP::ReturnType>::get(),
+ reinterpret_cast<GenericFunction>(&GP::template get<ClassType>),
+ GP::getContext(getter),
+ TypeID<typename SP::ArgumentType>::get(),
+ reinterpret_cast<GenericFunction>(&SP::template set<ClassType>),
+ SP::getContext(setter));
+ return *this;
+ }
+
template<typename ReturnType, typename... Args, typename... Policies>
class_& class_function(const char* methodName, ReturnType (*classMethod)(Args...), Policies...) {
using namespace internal;
@@ -999,11 +1082,6 @@ namespace emscripten {
reinterpret_cast<GenericFunction>(classMethod));
return *this;
}
-
- template<typename ReturnType, typename... Args, typename... Policies>
- class_& calloperator(const char* methodName, Policies... policies) {
- return function(methodName, &ClassType::operator(), policies...);
- }
};
////////////////////////////////////////////////////////////////////////////////
@@ -1210,80 +1288,6 @@ namespace emscripten {
typename std::aligned_storage<sizeof(T)>::type data;
};
}
-
- ////////////////////////////////////////////////////////////////////////////////
- // NEW INTERFACE
- ////////////////////////////////////////////////////////////////////////////////
-
- class JSInterface {
- public:
- JSInterface(internal::EM_VAL handle) {
- initialize(handle);
- }
-
- JSInterface(const JSInterface& obj)
- : jsobj(obj.jsobj)
- {}
-
- template<typename ReturnType, typename... Args>
- ReturnType call(const char* name, Args... args) {
- assertInitialized();
- return Caller<ReturnType, Args...>::call(*jsobj, name, args...);
- }
-
- static std::shared_ptr<JSInterface> cloneToSharedPtr(const JSInterface& i) {
- return std::make_shared<JSInterface>(i);
- }
-
- private:
- void initialize(internal::EM_VAL handle) {
- if (jsobj) {
- internal::_embind_fatal_error(
- "Cannot initialize interface wrapper twice",
- "JSInterface");
- }
- jsobj = val::take_ownership(handle);
- }
-
- // this class only exists because you can't partially specialize function templates
- template<typename ReturnType, typename... Args>
- struct Caller {
- static ReturnType call(val& v, const char* name, Args... args) {
- return v.call(name, args...).template as<ReturnType>();
- }
- };
-
- template<typename... Args>
- struct Caller<void, Args...> {
- static void call(val& v, const char* name, Args... args) {
- v.call_void(name, args...);
- }
- };
-
- void assertInitialized() {
- if (!jsobj) {
- internal::_embind_fatal_error(
- "Cannot invoke call on uninitialized Javascript interface wrapper.", "JSInterface");
- }
- }
-
- internal::optional<val> jsobj;
- };
-
- namespace internal {
- extern JSInterface* create_js_interface(EM_VAL e);
- }
-
- class register_js_interface {
- public:
- register_js_interface() {
- _embind_register_interface(
- internal::TypeID<JSInterface>::get(),
- "JSInterface",
- reinterpret_cast<internal::GenericFunction>(&internal::create_js_interface),
- reinterpret_cast<internal::GenericFunction>(&internal::raw_destructor<JSInterface>));
- }
- };
}
namespace emscripten {
diff --git a/system/include/emscripten/val.h b/system/include/emscripten/val.h
index 91f775a7..09cad80e 100644
--- a/system/include/emscripten/val.h
+++ b/system/include/emscripten/val.h
@@ -8,6 +8,8 @@ namespace emscripten {
namespace internal {
// Implemented in JavaScript. Don't call these directly.
extern "C" {
+ void _emval_register_symbol(const char*);
+
typedef struct _EM_VAL* EM_VAL;
void _emval_incref(EM_VAL value);
@@ -48,9 +50,23 @@ namespace emscripten {
unsigned argCount,
internal::TYPEID argTypes[]
/*, ...*/);
+ bool _emval_has_function(
+ EM_VAL value,
+ const char* methodName);
}
}
+ template<const char* address>
+ struct symbol_registrar {
+ symbol_registrar() {
+ internal::_emval_register_symbol(address);
+ }
+ };
+
+#define EMSCRIPTEN_SYMBOL(name) \
+ static const char name##_symbol[] = #name; \
+ static const symbol_registrar<name##_symbol> name##_registrar
+
class val {
public:
// missing operators:
@@ -98,18 +114,24 @@ namespace emscripten {
}
template<typename T>
- explicit val(const T& value) {
+ explicit val(T&& value) {
typedef internal::BindingType<T> BT;
auto taker = reinterpret_cast<internal::EM_VAL (*)(internal::TYPEID, typename BT::WireType)>(&internal::_emval_take_value);
- handle = taker(internal::TypeID<T>::get(), BT::toWireType(value));
+ handle = taker(internal::TypeID<T>::get(), BT::toWireType(std::forward<T>(value)));
}
val() = delete;
- val(const char* v)
+ explicit val(const char* v)
: handle(internal::_emval_new_cstring(v))
{}
+ val(val&& v)
+ : handle(v.handle)
+ {
+ v.handle = 0;
+ }
+
val(const val& v)
: handle(v.handle)
{
@@ -120,6 +142,13 @@ namespace emscripten {
internal::_emval_decref(handle);
}
+ val& operator=(val&& v) {
+ internal::_emval_decref(handle);
+ handle = v.handle;
+ v.handle = 0;
+ return *this;
+ }
+
val& operator=(const val& v) {
internal::_emval_incref(v.handle);
internal::_emval_decref(handle);
@@ -132,7 +161,7 @@ namespace emscripten {
}
template<typename... Args>
- val new_(Args... args) const {
+ val new_(Args&&... args) const {
using namespace internal;
WithPolicies<>::ArgTypeList<Args...> argList;
@@ -149,7 +178,7 @@ namespace emscripten {
handle,
argList.count,
argList.types,
- toWireType(args)...));
+ toWireType(std::forward<Args>(args))...));
}
template<typename T>
@@ -163,7 +192,7 @@ namespace emscripten {
}
template<typename... Args>
- val operator()(Args... args) {
+ val operator()(Args&&... args) {
using namespace internal;
WithPolicies<>::ArgTypeList<Args...> argList;
@@ -178,11 +207,11 @@ namespace emscripten {
handle,
argList.count,
argList.types,
- toWireType(args)...));
+ toWireType(std::forward<Args>(args))...));
}
template<typename ...Args>
- val call(const char* name, Args... args) const {
+ val call(const char* name, Args&&... args) const {
using namespace internal;
WithPolicies<>::ArgTypeList<Args...> argList;
@@ -199,11 +228,11 @@ namespace emscripten {
name,
argList.count,
argList.types,
- toWireType(args)...));
+ toWireType(std::forward<Args>(args))...));
}
template<typename ...Args>
- void call_void(const char* name, Args... args) const {
+ void call_void(const char* name, Args&&... args) const {
using namespace internal;
WithPolicies<>::ArgTypeList<Args...> argList;
@@ -219,7 +248,11 @@ namespace emscripten {
name,
argList.count,
argList.types,
- toWireType(args)...);
+ toWireType(std::forward<Args>(args))...);
+ }
+
+ bool has_function(const char* name) const {
+ return _emval_has_function(handle, name);
}
template<typename T>
@@ -253,7 +286,7 @@ namespace emscripten {
template<>
struct BindingType<val> {
typedef internal::EM_VAL WireType;
- static WireType toWireType(val v) {
+ static WireType toWireType(const val& v) {
_emval_incref(v.handle);
return v.handle;
}
diff --git a/system/include/emscripten/wire.h b/system/include/emscripten/wire.h
index 64114491..6fb15fc7 100755
--- a/system/include/emscripten/wire.h
+++ b/system/include/emscripten/wire.h
@@ -181,11 +181,35 @@ namespace emscripten {
}
};
+ template<>
+ struct BindingType<std::wstring> {
+ typedef struct {
+ size_t length;
+ wchar_t data[1]; // trailing data
+ }* WireType;
+ static WireType toWireType(const std::wstring& v) {
+ WireType wt = (WireType)malloc(sizeof(size_t) + v.length() * sizeof(wchar_t));
+ wt->length = v.length();
+ wmemcpy(wt->data, v.data(), v.length());
+ return wt;
+ }
+ static std::wstring fromWireType(WireType v) {
+ return std::wstring(v->data, v->length);
+ }
+ static void destroy(WireType v) {
+ free(v);
+ }
+ };
+
template<typename T>
struct BindingType<const T> : public BindingType<T> {
};
template<typename T>
+ struct BindingType<T&> : public BindingType<T> {
+ };
+
+ template<typename T>
struct BindingType<const T&> : public BindingType<T> {
};
@@ -216,10 +240,14 @@ namespace emscripten {
typedef typename std::remove_reference<T>::type ActualT;
typedef ActualT* WireType;
- static WireType toWireType(T v) {
+ static WireType toWireType(const T& v) {
return new T(v);
}
+ static WireType toWireType(T&& v) {
+ return new T(std::forward<T>(v));
+ }
+
static ActualT& fromWireType(WireType p) {
return *p;
}
@@ -262,8 +290,8 @@ namespace emscripten {
{};
template<typename T>
- auto toWireType(const T& v) -> typename BindingType<T>::WireType {
- return BindingType<T>::toWireType(v);
+ auto toWireType(T&& v) -> typename BindingType<T>::WireType {
+ return BindingType<T>::toWireType(std::forward<T>(v));
}
template<typename T>
diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp
index deb55138..35d99dad 100755
--- a/system/lib/embind/bind.cpp
+++ b/system/lib/embind/bind.cpp
@@ -36,12 +36,22 @@ extern "C" {
}
}
-namespace emscripten {
- namespace internal {
- JSInterface* create_js_interface(EM_VAL e) {
- return new JSInterface(e);
- }
+// TODO: fix in library.js or a proper emscripten libc
+extern "C" wchar_t *wmemset(wchar_t *dest, wchar_t c, size_t count) {
+ wchar_t *o = dest;
+ while (count--) {
+ *o++ = c;
+ }
+ return dest;
+}
+
+// TODO: fix in library.js or a proper emscripten libc
+extern "C" wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, size_t count) {
+ wchar_t *o = dest;
+ while (count--) {
+ *dest++ = *src++;
}
+ return dest;
}
EMSCRIPTEN_BINDINGS(native_and_builtin_types) {
@@ -64,6 +74,7 @@ EMSCRIPTEN_BINDINGS(native_and_builtin_types) {
_embind_register_float(TypeID<float>::get(), "float");
_embind_register_float(TypeID<double>::get(), "double");
- _embind_register_cstring(TypeID<std::string>::get(), "std::string");
+ _embind_register_std_string(TypeID<std::string>::get(), "std::string");
+ _embind_register_std_wstring(TypeID<std::wstring>::get(), sizeof(wchar_t), "std::wstring");
_embind_register_emval(TypeID<val>::get(), "emscripten::val");
}
diff --git a/tests/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll
index 7e17c707..89ebcef6 100644
--- a/tests/cases/legalizer_ta2.ll
+++ b/tests/cases/legalizer_ta2.ll
@@ -188,4 +188,5 @@ done:
declare i32 @puts(i8*)
declare i32 @__gxx_personality_v0(...)
+declare void @__cxa_throw(i32, i32, i32) ; for asm1, where exceptions are enabled but this test needs a throw to bring in lib stuff
diff --git a/tests/cases/longjmp_tiny_noasm.ll b/tests/cases/longjmp_tiny.ll
index 0045847c..0045847c 100644
--- a/tests/cases/longjmp_tiny_noasm.ll
+++ b/tests/cases/longjmp_tiny.ll
diff --git a/tests/cases/longjmp_tiny_noasm.txt b/tests/cases/longjmp_tiny.txt
index 8a0aa386..8a0aa386 100644
--- a/tests/cases/longjmp_tiny_noasm.txt
+++ b/tests/cases/longjmp_tiny.txt
diff --git a/tests/cases/longjmp_tiny_noasm_invoke.ll b/tests/cases/longjmp_tiny_invoke.ll
index e1a72e00..e1a72e00 100644
--- a/tests/cases/longjmp_tiny_noasm_invoke.ll
+++ b/tests/cases/longjmp_tiny_invoke.ll
diff --git a/tests/cases/longjmp_tiny_noasm_invoke.txt b/tests/cases/longjmp_tiny_invoke.txt
index 8a0aa386..8a0aa386 100644
--- a/tests/cases/longjmp_tiny_noasm_invoke.txt
+++ b/tests/cases/longjmp_tiny_invoke.txt
diff --git a/tests/cases/longjmp_tiny_phi_noasm.ll b/tests/cases/longjmp_tiny_phi.ll
index cced7cab..cced7cab 100644
--- a/tests/cases/longjmp_tiny_phi_noasm.ll
+++ b/tests/cases/longjmp_tiny_phi.ll
diff --git a/tests/cases/longjmp_tiny_phi_noasm.txt b/tests/cases/longjmp_tiny_phi.txt
index 16f5a93e..16f5a93e 100644
--- a/tests/cases/longjmp_tiny_phi_noasm.txt
+++ b/tests/cases/longjmp_tiny_phi.txt
diff --git a/tests/cases/longjmp_tiny_phi2_noasm.ll b/tests/cases/longjmp_tiny_phi2.ll
index 1d7761c3..1d7761c3 100644
--- a/tests/cases/longjmp_tiny_phi2_noasm.ll
+++ b/tests/cases/longjmp_tiny_phi2.ll
diff --git a/tests/cases/longjmp_tiny_phi2_noasm.txt b/tests/cases/longjmp_tiny_phi2.txt
index 37e85737..37e85737 100644
--- a/tests/cases/longjmp_tiny_phi2_noasm.txt
+++ b/tests/cases/longjmp_tiny_phi2.txt
diff --git a/tests/embind/embind.benchmark.js b/tests/embind/embind.benchmark.js
new file mode 100644
index 00000000..4ce9355c
--- /dev/null
+++ b/tests/embind/embind.benchmark.js
@@ -0,0 +1,201 @@
+function _increment_counter_benchmark_js(N) {
+ var ctr = _get_counter();
+ var a = _emscripten_get_now();
+ for(i = 0; i < N; ++i) {
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ _increment_counter();
+ }
+ var b = _emscripten_get_now();
+ var ctr2 = _get_counter();
+ Module.print("JS increment_counter " + N + " iters: " + (b-a) + " msecs. result: " + (ctr2-ctr));
+}
+
+function _increment_class_counter_benchmark_embind_js(N) {
+ var foo = new Module['Foo']();
+ var a = _emscripten_get_now();
+ for(i = 0; i < N; ++i) {
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ foo['incr_class_counter']();
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS embind increment_class_counter " + N + " iters: " + (b-a) + " msecs. result: " + foo['class_counter_val']());
+ foo['delete']();
+}
+
+function _returns_input_benchmark_js() {
+ var a = _emscripten_get_now();
+ var t = 0;
+ for(i = 0; i < 100000; ++i) {
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ t += _returns_input(i);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS returns_input 100000 iters: " + (b-a) + " msecs. result: " + t);
+}
+
+function _sum_int_benchmark_js() {
+ var a = _emscripten_get_now();
+ var r = 0;
+ for(i = 0; i < 100000; ++i) {
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_int(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS sum_int 100000 iters: " + (b-a) + " msecs. result: " + r);
+}
+
+function _sum_float_benchmark_js() {
+ var a = _emscripten_get_now();
+ var r = 0;
+ for(i = 0; i < 100000; ++i) {
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += _sum_float(i, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS sum_float 100000 iters: " + (b-a) + " msecs. result: " + r);
+}
+
+function _increment_counter_benchmark_embind_js(N) {
+ var ctr = _get_counter();
+ var a = _emscripten_get_now();
+ for(i = 0; i < N; ++i) {
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ Module['increment_counter']();
+ }
+ var b = _emscripten_get_now();
+ var ctr2 = _get_counter();
+ Module.print("JS embind increment_counter " + N + " iters: " + (b-a) + " msecs. result: " + (ctr2-ctr));
+}
+
+function _returns_input_benchmark_embind_js() {
+ var a = _emscripten_get_now();
+ var t = 0;
+ for(i = 0; i < 100000; ++i) {
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ t += Module['returns_input'](i);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS embind returns_input 100000 iters: " + (b-a) + " msecs. result: " + t);
+}
+
+function _sum_int_benchmark_embind_js() {
+ var a = _emscripten_get_now();
+ var r = 0;
+ for(i = 0; i < 100000; ++i) {
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_int'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS embind sum_int 100000 iters: " + (b-a) + " msecs. result: " + r);
+}
+
+function _sum_float_benchmark_embind_js() {
+ var a = _emscripten_get_now();
+ var r = 0;
+ for(i = 0; i < 100000; ++i) {
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ r += Module['sum_float'](i, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
+ var b = _emscripten_get_now();
+ Module.print("JS embind sum_float 100000 iters: " + (b-a) + " msecs. result: " + r);
+}
+
+function _move_gameobjects_benchmark_embind_js() {
+ var N = 10000;
+ var objects = [];
+ for(i = 0; i < N; ++i) {
+ objects.push(Module['create_game_object']());
+ }
+
+ var a = _emscripten_get_now();
+ for(i = 0; i < N; ++i) {
+ var t = objects[i]['GetTransform']();
+ var pos = Module['add'](t['GetPosition'](), [2, 0, 1]);
+ var rot = Module['add'](t['GetRotation'](), [0.1, 0.2, 0.3]);
+ t['SetPosition'](pos);
+ t['SetRotation'](rot);
+ t['delete']();
+ }
+ var b = _emscripten_get_now();
+
+ var accum = [0,0,0];
+ for(i = 0; i < N; ++i) {
+ var t = objects[i]['GetTransform']();
+ accum = Module['add'](Module['add'](accum, t['GetPosition']()), t['GetRotation']());
+ t['delete']();
+ }
+
+ Module.print("JS embind move_gameobjects " + N + " iters: " + (b-a) + " msecs. Result: " + (accum[0] + accum[1] + accum[2]));
+}
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 290fed72..8ef46ad8 100755
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -143,33 +143,49 @@ module({
var e = assert.throws(cm.BindingError, function() {
cm.Derived.prototype.setMember.call(a, "foo");
});
- assert.equal('Derived.setMember incompatible with "this" of type HasTwoBases', e.message);
+ assert.equal('Expected null or instance of Derived, got an instance of Base2', e.message);
a.delete();
+
+ // Base1 and Base2 both have the method 'getField()' exposed - make sure
+ // that calling the Base2 function with a 'this' instance of Base1 doesn't accidentally work!
+ var b = new cm.Base1;
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Base2.prototype.getField.call(b);
+ });
+ assert.equal('Expected null or instance of Base2, got an instance of Base1', e.message);
+ b.delete();
});
test("calling method with invalid this throws error", function() {
var e = assert.throws(cm.BindingError, function() {
cm.Derived.prototype.setMember.call(undefined, "foo");
});
- if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
- // got Error: expected: Derived.setMember with invalid "this": undefined, actual: Derived.setMember incompatible with "this" of type Object
- assert.equal('Derived.setMember with invalid "this": undefined', e.message);
- }
+ assert.equal('Cannot pass "[object global]" as a Derived*', e.message);
+
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call(true, "foo");
+ });
+ assert.equal('Cannot pass "true" as a Derived*', e.message);
+
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call(null, "foo");
+ });
+ assert.equal('Cannot pass "[object global]" as a Derived*', e.message);
+
+ var e = assert.throws(cm.BindingError, function() {
+ cm.Derived.prototype.setMember.call(42, "foo");
+ });
+ assert.equal('Cannot pass "42" as a Derived*', e.message);
var e = assert.throws(cm.BindingError, function() {
cm.Derived.prototype.setMember.call("this", "foo");
});
- if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
- // TODO got 'Derived.setMember incompatible with "this" of type Object'
- assert.equal('Derived.setMember with invalid "this": this', e.message);
- }
+ assert.equal('Cannot pass "this" as a Derived*', e.message);
var e = assert.throws(cm.BindingError, function() {
cm.Derived.prototype.setMember.call({}, "foo");
});
- if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
- assert.equal('Derived.setMember incompatible with "this" of type Object', e.message);
- }
+ assert.equal('Cannot pass "[object Object]" as a Derived*', e.message);
});
test("setting and getting property on unrelated class throws error", function() {
@@ -388,6 +404,59 @@ module({
});
});
+ BaseFixture.extend("string", function() {
+ test("non-ascii strings", function() {
+ var expected = '';
+ for (var i = 0; i < 128; ++i) {
+ expected += String.fromCharCode(128 + i);
+ }
+ assert.equal(expected, cm.get_non_ascii_string());
+ });
+
+ test("passing non-8-bit strings from JS to std::string throws", function() {
+ assert.throws(cm.BindingError, function() {
+ cm.emval_test_take_and_return_std_string("\u1234");
+ });
+ });
+
+ test("can't pass integers as strings", function() {
+ var e = assert.throws(cm.BindingError, function() {
+ cm.emval_test_take_and_return_std_string(10);
+ });
+ });
+
+ test("can pass Uint8Array to std::string", function() {
+ var e = cm.emval_test_take_and_return_std_string(new Uint8Array([65, 66, 67, 68]));
+ assert.equal('ABCD', e);
+ });
+
+ test("can pass Int8Array to std::string", function() {
+ var e = cm.emval_test_take_and_return_std_string(new Int8Array([65, 66, 67, 68]));
+ assert.equal('ABCD', e);
+ });
+
+ test("can pass ArrayBuffer to std::string", function() {
+ var e = cm.emval_test_take_and_return_std_string((new Int8Array([65, 66, 67, 68])).buffer);
+ assert.equal('ABCD', e);
+ });
+
+ test("non-ascii wstrings", function() {
+ var expected = String.fromCharCode(10) +
+ String.fromCharCode(1234) +
+ String.fromCharCode(2345) +
+ String.fromCharCode(65535);
+ assert.equal(expected, cm.get_non_ascii_wstring());
+ });
+
+ test("passing unicode string into C++", function() {
+ var expected = String.fromCharCode(10) +
+ String.fromCharCode(1234) +
+ String.fromCharCode(2345) +
+ String.fromCharCode(65535);
+ assert.equal(expected, cm.take_and_return_std_wstring(expected));
+ });
+ });
+
BaseFixture.extend("embind", function() {
test("value creation", function() {
assert.equal(15, cm.emval_test_new_integer());
@@ -837,6 +906,37 @@ module({
assert.equal(0, cm.count_emval_handles());
});
+ test("class properties can be methods", function() {
+ var a = {};
+ var b = {foo: 'foo'};
+ var c = new cm.ValHolder(a);
+ assert.equal(a, c.val);
+ c.val = b;
+ assert.equal(b, c.val);
+ c.delete();
+ });
+
+ test("class properties can be read-only", function() {
+ var a = {};
+ var h = new cm.ValHolder(a);
+ assert.equal(a, h.val_readonly);
+ var e = assert.throws(cm.BindingError, function() {
+ h.val_readonly = 10;
+ });
+ assert.equal('ValHolder.val_readonly is a read-only property', e.message);
+ h.delete();
+ });
+
+ test("read-only member field", function() {
+ var a = new cm.HasReadOnlyProperty(10);
+ assert.equal(10, a.i);
+ var e = assert.throws(cm.BindingError, function() {
+ a.i = 20;
+ });
+ assert.equal('HasReadOnlyProperty.i is a read-only property', e.message);
+ a.delete();
+ });
+
test("class instance $$ property is non-enumerable", function() {
var c = new cm.ValHolder(undefined);
assert.deepEqual([], Object.keys(c));
@@ -1415,24 +1515,6 @@ module({
});
});
- BaseFixture.extend("JavaScript interface", function() {
- this.setUp(function() {
- this.testobj = {
- "method1": function() { return 111; },
- "method2": function() { return 222; }
- };
- });
-
- test("pass js object to c++ and call its method", function() {
- var obj = new cm.JSInterfaceHolder(this.testobj);
- assert.equal(111, obj.callMethod("method1"));
- assert.equal(222, obj.callMethod("method2"));
- assert.equal(111, obj.callMethodUsingSharedPtr("method1"));
- assert.equal(222, obj.callMethodUsingSharedPtr("method2"));
- obj.delete();
- });
- });
-
BaseFixture.extend("abstract methods", function() {
test("can call abstract methods", function() {
var obj = cm.getAbstractClass();
@@ -1454,6 +1536,28 @@ module({
assert.equal(expected, cm.callAbstractMethod(impl));
impl.delete();
});
+
+ test("can implement optional methods in JavaScript", function() {
+ var expected = "my JS string";
+ function MyImplementation() {
+ this.rv = expected;
+ }
+ MyImplementation.prototype.optionalMethod = function() {
+ return this.rv;
+ };
+
+ var impl = cm.AbstractClass.implement(new MyImplementation);
+ assert.equal(expected, impl.optionalMethod(expected));
+ assert.equal(expected, cm.callOptionalMethod(impl, expected));
+ impl.delete();
+ });
+
+ test("if not implemented then optional method runs default", function() {
+ var impl = cm.AbstractClass.implement({});
+ assert.equal("optionalfoo", impl.optionalMethod("foo"));
+ assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
+ impl.delete();
+ });
});
BaseFixture.extend("registration order", function() {
diff --git a/tests/embind/embind_benchmark.cpp b/tests/embind/embind_benchmark.cpp
new file mode 100644
index 00000000..80abc7e7
--- /dev/null
+++ b/tests/embind/embind_benchmark.cpp
@@ -0,0 +1,344 @@
+#include <stdio.h>
+#include <emscripten.h>
+#include <bind.h>
+#include <memory>
+
+int counter = 0;
+
+extern "C"
+{
+
+int __attribute__((noinline)) get_counter()
+{
+ return counter;
+}
+
+void __attribute__((noinline)) increment_counter()
+{
+ ++counter;
+}
+
+int __attribute__((noinline)) sum_int(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int v9)
+{
+ return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;
+}
+
+float __attribute__((noinline)) sum_float(float v1, float v2, float v3, float v4, float v5, float v6, float v7, float v8, float v9)
+{
+ return v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9;
+}
+
+int __attribute__((noinline)) returns_input(int i)
+{
+ return i;
+}
+
+extern void increment_counter_benchmark_js(int N);
+extern void returns_input_benchmark_js();
+extern void sum_int_benchmark_js();
+extern void sum_float_benchmark_js();
+
+extern void increment_counter_benchmark_embind_js(int N);
+extern void returns_input_benchmark_embind_js();
+extern void sum_int_benchmark_embind_js();
+extern void sum_float_benchmark_embind_js();
+
+extern void increment_class_counter_benchmark_embind_js(int N);
+extern void move_gameobjects_benchmark_embind_js();
+}
+
+class Vec3
+{
+public:
+ Vec3():x(0),y(0),z(0) {}
+ Vec3(float x_, float y_, float z_):x(x_),y(y_),z(z_) {}
+ float x,y,z;
+};
+
+Vec3 add(const Vec3 &lhs, const Vec3 &rhs) { return Vec3(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z); }
+
+class Transform
+{
+public:
+ Transform():scale(1) {}
+
+ Vec3 pos;
+ Vec3 rot;
+ float scale;
+
+ Vec3 __attribute__((noinline)) GetPosition() const { return pos; }
+ Vec3 __attribute__((noinline)) GetRotation() const { return rot; }
+ float __attribute__((noinline)) GetScale() const { return scale; }
+
+ void __attribute__((noinline)) SetPosition(const Vec3 &pos_) { pos = pos_; }
+ void __attribute__((noinline)) SetRotation(const Vec3 &rot_) { rot = rot_; }
+ void __attribute__((noinline)) SetScale(float scale_) { scale = scale_; }
+};
+typedef std::shared_ptr<Transform> TransformPtr;
+
+class GameObject
+{
+public:
+ GameObject()
+ {
+ transform = std::make_shared<Transform>();
+ }
+ std::shared_ptr<Transform> transform;
+
+ TransformPtr __attribute__((noinline)) GetTransform() const { return transform; }
+};
+typedef std::shared_ptr<GameObject> GameObjectPtr;
+
+GameObjectPtr create_game_object()
+{
+ return std::make_shared<GameObject>();
+}
+
+class Foo
+{
+public:
+ Foo()
+ :class_counter(0)
+ {
+ }
+
+ void __attribute__((noinline)) incr_global_counter()
+ {
+ ++counter;
+ }
+
+ void __attribute__((noinline)) incr_class_counter()
+ {
+ ++class_counter;
+ }
+
+ int class_counter_val() const
+ {
+ return class_counter;
+ }
+
+ int class_counter;
+};
+
+EMSCRIPTEN_BINDINGS(benchmark)
+{
+ using namespace emscripten;
+
+ class_<GameObject>("GameObject")
+ .smart_ptr<GameObjectPtr>()
+ .function("GetTransform", &GameObject::GetTransform);
+
+ class_<Transform>("Transform")
+ .smart_ptr<TransformPtr>()
+ .function("GetPosition", &Transform::GetPosition)
+ .function("GetRotation", &Transform::GetRotation)
+ .function("GetScale", &Transform::GetScale)
+ .function("SetPosition", &Transform::SetPosition)
+ .function("SetRotation", &Transform::SetRotation)
+ .function("SetScale", &Transform::SetScale);
+
+ value_tuple<Vec3>("Vec3")
+ .element(&Vec3::x)
+ .element(&Vec3::y)
+ .element(&Vec3::z);
+
+ function("create_game_object", &create_game_object);
+ function("add", &add);
+
+ function("get_counter", &get_counter);
+ function("increment_counter", &increment_counter);
+ function("returns_input", &returns_input);
+ function("sum_int", &sum_int);
+ function("sum_float", &sum_float);
+
+ class_<Foo>("Foo")
+ .constructor<>()
+ .function("incr_global_counter", &Foo::incr_global_counter)
+ .function("incr_class_counter", &Foo::incr_class_counter)
+ .function("class_counter_val", &Foo::class_counter_val);
+}
+
+void __attribute__((noinline)) emscripten_get_now_benchmark(int N)
+{
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < N; ++i)
+ {
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ emscripten_get_now();
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ emscripten_get_now %d iters: %f msecs.\n", N, (t2-t));
+}
+
+void __attribute__((noinline)) increment_counter_benchmark(int N)
+{
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < N; ++i)
+ {
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ increment_counter();
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ increment_counter %d iters: %f msecs.\n", N, (t2-t));
+}
+
+void __attribute__((noinline)) increment_class_counter_benchmark(int N)
+{
+ Foo foo;
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < N; ++i)
+ {
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ foo.incr_class_counter();
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ increment_class_counter %d iters: %f msecs. result: %d\n", N, (t2-t), foo.class_counter);
+}
+
+void __attribute__((noinline)) returns_input_benchmark()
+{
+ volatile int r = 0;
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < 100000; ++i)
+ {
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ r += returns_input(i);
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ returns_input 100000 iters: %f msecs.\n", (t2-t));
+}
+
+void __attribute__((noinline)) sum_int_benchmark()
+{
+ volatile float t = emscripten_get_now();
+ volatile int r = 0;
+ for(int i = 0; i < 100000; ++i)
+ {
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ r += sum_int(i,2,3,4,5,6,7,8,9);
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ sum_int 100000 iters: %f msecs.\n", (t2-t));
+}
+
+void __attribute__((noinline)) sum_float_benchmark()
+{
+ volatile float f = 0.f;
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < 100000; ++i)
+ {
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ f += sum_float((float)i,2.f,3.f,4.f,5.f,6.f,7.f,8.f,9.f);
+ }
+ volatile float t2 = emscripten_get_now();
+ printf("C++ sum_float 100000 iters: %f msecs.\n", (t2-t));
+}
+
+void __attribute__((noinline)) move_gameobjects_benchmark()
+{
+ const int N = 10000;
+ GameObjectPtr objects[N];
+ for(int i = 0; i < N; ++i)
+ objects[i] = create_game_object();
+
+ volatile float t = emscripten_get_now();
+ for(int i = 0; i < N; ++i)
+ {
+ TransformPtr t = objects[i]->GetTransform();
+ Vec3 pos = add(t->GetPosition(), Vec3(2.f, 0.f, 1.f));
+ Vec3 rot = add(t->GetRotation(), Vec3(0.1f, 0.2f, 0.3f));
+ t->SetPosition(pos);
+ t->SetRotation(rot);
+ }
+ volatile float t2 = emscripten_get_now();
+
+ Vec3 accum;
+ for(int i = 0; i < N; ++i)
+ accum = add(add(accum, objects[i]->GetTransform()->GetPosition()), objects[i]->GetTransform()->GetRotation());
+ printf("C++ move_gameobjects %d iters: %f msecs. Result: %f\n", N, (t2-t), accum.x+accum.y+accum.z);
+}
+
+int main()
+{
+ for(int i = 1000; i <= 100000; i *= 10)
+ emscripten_get_now_benchmark(i);
+
+ printf("\n");
+ for(int i = 1000; i <= 100000; i *= 10)
+ {
+ increment_counter_benchmark(i);
+ increment_counter_benchmark_js(i);
+ increment_counter_benchmark_embind_js(i);
+ printf("\n");
+ }
+
+ for(int i = 1000; i <= 100000; i *= 10)
+ {
+ increment_class_counter_benchmark(i);
+ increment_class_counter_benchmark_embind_js(i);
+ printf("\n");
+ }
+
+ returns_input_benchmark();
+ returns_input_benchmark_js();
+ returns_input_benchmark_embind_js();
+ printf("\n");
+ sum_int_benchmark();
+ sum_int_benchmark_js();
+ sum_int_benchmark_embind_js();
+ printf("\n");
+ sum_float_benchmark();
+ sum_float_benchmark_js();
+ sum_float_benchmark_embind_js();
+ printf("\n");
+ move_gameobjects_benchmark();
+ move_gameobjects_benchmark_embind_js();
+}
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index 1384406a..f2359955 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -79,6 +79,24 @@ unsigned emval_test_sum(val v) {
return rv;
}
+std::string get_non_ascii_string() {
+ char c[128 + 1];
+ c[128] = 0;
+ for (int i = 0; i < 128; ++i) {
+ c[i] = 128 + i;
+ }
+ return c;
+}
+
+std::wstring get_non_ascii_wstring() {
+ std::wstring ws(4, 0);
+ ws[0] = 10;
+ ws[1] = 1234;
+ ws[2] = 2345;
+ ws[3] = 65535;
+ return ws;
+}
+
std::string emval_test_take_and_return_const_char_star(const char* str) {
return str;
}
@@ -91,6 +109,10 @@ std::string emval_test_take_and_return_std_string_const_ref(const std::string& s
return str;
}
+std::wstring take_and_return_std_wstring(std::wstring str) {
+ return str;
+}
+
std::function<std::string (std::string)> emval_test_get_function_ptr() {
return emval_test_take_and_return_std_string;
}
@@ -651,8 +673,6 @@ private:
std::string name_;
};
-// todo: does it need to be polymorphic?
-// todo: virtual diamond pattern
class PolyDiamondBase {
public:
PolyDiamondBase():
@@ -1043,20 +1063,6 @@ std::vector<std::shared_ptr<StringHolder>> emval_test_return_shared_ptr_vector()
return sharedStrVector;
}
-class JSInterfaceHolder {
-public:
- JSInterfaceHolder(JSInterface &jsobj) : jsobj_(jsobj) {
- ptr_ = JSInterface::cloneToSharedPtr(jsobj_);
- }
-
- int callMethod(std::string method) { return jsobj_.call<int>(method.c_str()); }
- int callMethodUsingSharedPtr(std::string method) { return ptr_->call<int>(method.c_str()); }
-
-private:
- JSInterface jsobj_;
- std::shared_ptr<JSInterface> ptr_;
-};
-
void test_string_with_vec(const std::string& p1, std::vector<std::string>& v1) {
// THIS DOES NOT WORK -- need to get as val and then call vecFromJSArray
printf("%s\n", p1.c_str());
@@ -1074,8 +1080,13 @@ class AbstractClass {
public:
virtual ~AbstractClass() {}
virtual std::string abstractMethod() const = 0;
+ virtual std::string optionalMethod(std::string s) const {
+ return "optional" + s;
+ }
};
+EMSCRIPTEN_SYMBOL(optionalMethod);
+
class AbstractClassWrapper : public wrapper<AbstractClass> {
public:
EMSCRIPTEN_WRAPPER(AbstractClassWrapper);
@@ -1083,6 +1094,11 @@ public:
std::string abstractMethod() const {
return call<std::string>("abstractMethod");
}
+ std::string optionalMethod(std::string s) const {
+ return optional_call<std::string>(optionalMethod_symbol, [&] {
+ return AbstractClass::optionalMethod(s);
+ }, s);
+ }
};
class ConcreteClass : public AbstractClass {
@@ -1099,6 +1115,10 @@ std::string callAbstractMethod(AbstractClass& ac) {
return ac.abstractMethod();
}
+std::string callOptionalMethod(AbstractClass& ac, std::string s) {
+ return ac.optionalMethod(s);
+}
+
class HasExternalConstructor {
public:
HasExternalConstructor(const std::string& str)
@@ -1454,8 +1474,6 @@ EMSCRIPTEN_BINDINGS(constants) {
}
EMSCRIPTEN_BINDINGS(tests) {
- register_js_interface();
-
register_vector<int>("IntegerVector");
register_vector<char>("CharVector");
register_vector<unsigned>("VectorUnsigned");
@@ -1481,9 +1499,12 @@ EMSCRIPTEN_BINDINGS(tests) {
function("const_ref_adder", &const_ref_adder);
function("emval_test_sum", &emval_test_sum);
+ function("get_non_ascii_string", &get_non_ascii_string);
+ function("get_non_ascii_wstring", &get_non_ascii_wstring);
//function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
function("emval_test_take_and_return_std_string", &emval_test_take_and_return_std_string);
function("emval_test_take_and_return_std_string_const_ref", &emval_test_take_and_return_std_string_const_ref);
+ function("take_and_return_std_wstring", &take_and_return_std_wstring);
//function("emval_test_take_and_return_CustomStruct", &emval_test_take_and_return_CustomStruct);
@@ -1527,6 +1548,8 @@ EMSCRIPTEN_BINDINGS(tests) {
.function("getConstVal", &ValHolder::getConstVal)
.function("getValConstRef", &ValHolder::getValConstRef)
.function("setVal", &ValHolder::setVal)
+ .property("val", &ValHolder::getVal, &ValHolder::setVal)
+ .property("val_readonly", &ValHolder::getVal)
.class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer<ret_val>())
.class_function("makeValHolder", &ValHolder::makeValHolder)
.class_function("some_class_method", &ValHolder::some_class_method)
@@ -1550,7 +1573,7 @@ EMSCRIPTEN_BINDINGS(tests) {
class_<std::function<std::string(std::string)>>("StringFunctorString")
.constructor<>()
- .calloperator<std::string, std::string>("opcall")
+ .function("opcall", &std::function<std::string(std::string)>::operator())
;
function("emval_test_get_function_ptr", &emval_test_get_function_ptr);
@@ -1790,6 +1813,11 @@ EMSCRIPTEN_BINDINGS(tests) {
function("embind_attempt_to_modify_smart_pointer_when_passed_by_value", embind_attempt_to_modify_smart_pointer_when_passed_by_value);
function("embind_save_smart_base_pointer", embind_save_smart_base_pointer);
+ class_<Base1>("Base1")
+ .constructor()
+ .function("getField", &Base1::getField)
+ ;
+
class_<Base2>("Base2")
.function("getField", &Base2::getField)
.property("field", &Base2::field2)
@@ -1845,12 +1873,6 @@ EMSCRIPTEN_BINDINGS(tests) {
register_map<std::string, int>("StringIntMap");
function("embind_test_get_string_int_map", embind_test_get_string_int_map);
- class_<JSInterfaceHolder>("JSInterfaceHolder")
- .constructor<JSInterface&>()
- .function("callMethod", &JSInterfaceHolder::callMethod)
- .function("callMethodUsingSharedPtr", &JSInterfaceHolder::callMethodUsingSharedPtr)
- ;
-
function("embind_test_new_Object", &embind_test_new_Object);
function("embind_test_new_factory", &embind_test_new_factory);
@@ -1858,10 +1880,12 @@ EMSCRIPTEN_BINDINGS(tests) {
.smart_ptr<std::shared_ptr<AbstractClass>>()
.allow_subclass<AbstractClassWrapper>()
.function("abstractMethod", &AbstractClass::abstractMethod)
+ .function("optionalMethod", &AbstractClass::optionalMethod)
;
function("getAbstractClass", &getAbstractClass);
function("callAbstractMethod", &callAbstractMethod);
+ function("callOptionalMethod", &callOptionalMethod);
class_<HasExternalConstructor>("HasExternalConstructor")
.constructor(&createHasExternalConstructor)
@@ -1901,8 +1925,8 @@ EMSCRIPTEN_BINDINGS(tests) {
function("long_to_string", &long_to_string);
function("unsigned_long_to_string", &unsigned_long_to_string);
- function("overloaded_function", (int(*)(int))&overloaded_function);
- function("overloaded_function", (int(*)(int, int))&overloaded_function);
+ function("overloaded_function", select_overload<int(int)>(&overloaded_function));
+ function("overloaded_function", select_overload<int(int, int)>(&overloaded_function));
class_<MultipleCtors>("MultipleCtors")
.constructor<int>()
@@ -1912,19 +1936,21 @@ EMSCRIPTEN_BINDINGS(tests) {
class_<MultipleOverloads>("MultipleOverloads")
.constructor<>()
- .function("Func", (int(MultipleOverloads::*)(int))&MultipleOverloads::Func)
- .function("Func", (int(MultipleOverloads::*)(int,int))&MultipleOverloads::Func)
+ .function("Func", select_overload<int(int)>(&MultipleOverloads::Func))
+ .function("Func", select_overload<int(int, int)>(&MultipleOverloads::Func))
.function("WhichFuncCalled", &MultipleOverloads::WhichFuncCalled)
- .class_function("StaticFunc", (int(*)(int))&MultipleOverloads::StaticFunc)
- .class_function("StaticFunc", (int(*)(int,int))&MultipleOverloads::StaticFunc)
- .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled);
+ .class_function("StaticFunc", select_overload<int(int)>(&MultipleOverloads::StaticFunc))
+ .class_function("StaticFunc", select_overload<int(int,int)>(&MultipleOverloads::StaticFunc))
+ .class_function("WhichStaticFuncCalled", &MultipleOverloads::WhichStaticFuncCalled)
+ ;
class_<MultipleOverloadsDerived, base<MultipleOverloads> >("MultipleOverloadsDerived")
.constructor<>()
- .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int))&MultipleOverloadsDerived::Func)
- .function("Func", (int(MultipleOverloadsDerived::*)(int,int,int,int))&MultipleOverloadsDerived::Func)
- .class_function("StaticFunc", (int(*)(int,int,int))&MultipleOverloadsDerived::StaticFunc)
- .class_function("StaticFunc", (int(*)(int,int,int,int))&MultipleOverloadsDerived::StaticFunc);
+ .function("Func", select_overload<int(int,int,int)>(&MultipleOverloadsDerived::Func))
+ .function("Func", select_overload<int(int,int,int,int)>(&MultipleOverloadsDerived::Func))
+ .class_function("StaticFunc", select_overload<int(int,int,int)>(&MultipleOverloadsDerived::StaticFunc))
+ .class_function("StaticFunc", select_overload<int(int,int,int,int)>(&MultipleOverloadsDerived::StaticFunc))
+ ;
}
// tests for out-of-order registration
@@ -2048,15 +2074,41 @@ class Noncopyable {
public:
Noncopyable() {}
+ Noncopyable(Noncopyable&& other) {
+ other.valid = false;
+ }
std::string method() const {
return "foo";
}
+
+ bool valid = true;
};
+Noncopyable getNoncopyable() {
+ return Noncopyable();
+}
+
EMSCRIPTEN_BINDINGS(noncopyable) {
class_<Noncopyable>("Noncopyable")
.constructor<>()
.function("method", &Noncopyable::method)
;
+
+ function("getNoncopyable", &getNoncopyable);
+}
+
+struct HasReadOnlyProperty {
+ HasReadOnlyProperty(int i)
+ : i(i)
+ {}
+
+ const int i;
+};
+
+EMSCRIPTEN_BINDINGS(read_only_properties) {
+ class_<HasReadOnlyProperty>("HasReadOnlyProperty")
+ .constructor<int>()
+ .property("i", &HasReadOnlyProperty::i)
+ ;
}
diff --git a/tests/embind/shell.html b/tests/embind/shell.html
new file mode 100644
index 00000000..6664ec78
--- /dev/null
+++ b/tests/embind/shell.html
@@ -0,0 +1,94 @@
+<!doctype html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Emscripten-Generated Code</title>
+ <style>
+ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
+ textarea.emscripten { font-family: monospace; width: 80%; }
+ div.emscripten { text-align: center; }
+ div.emscripten_border { border: 1px solid black; }
+ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
+ canvas.emscripten { border: 0px none; }
+ </style>
+ </head>
+ <body>
+ <div class="emscripten" id="status">Downloading...</div>
+ <div class="emscripten">
+ <progress value="0" max="100" id="progress" hidden=1></progress>
+ </div>
+ <div class="emscripten_border" style="display:none;">
+ <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+ </div>
+ <div class="emscripten" style="display:none;">
+ <input type="checkbox" id="resize">Resize canvas
+ <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
+ &nbsp;&nbsp;&nbsp;
+ <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked,
+ document.getElementById('resize').checked)">
+ </div>
+
+ <hr/>
+ <textarea class="emscripten" id="output" rows="45"></textarea>
+ <hr>
+ <script type='text/javascript'>
+ // connect to canvas
+ var Module = {
+ preRun: [],
+ postRun: [],
+ print: (function() {
+ var element = document.getElementById('output');
+ element.value = ''; // clear browser cache
+ return function(text) {
+ text = Array.prototype.slice.call(arguments).join(' ');
+ // These replacements are necessary if you render to raw HTML
+ //text = text.replace(/&/g, "&amp;");
+ //text = text.replace(/</g, "&lt;");
+ //text = text.replace(/>/g, "&gt;");
+ //text = text.replace('\n', '<br>', 'g');
+ element.value += text + "\n";
+ element.scrollTop = 99999; // focus on bottom
+ };
+ })(),
+ printErr: function(text) {
+ text = Array.prototype.slice.call(arguments).join(' ');
+ if (0) { // XXX disabled for safety typeof dump == 'function') {
+ dump(text + '\n'); // fast, straight to the real console
+ } else {
+ console.log(text);
+ }
+ },
+ canvas: document.getElementById('canvas'),
+ setStatus: function(text) {
+ if (Module.setStatus.interval) clearInterval(Module.setStatus.interval);
+ var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
+ var statusElement = document.getElementById('status');
+ var progressElement = document.getElementById('progress');
+ if (m) {
+ text = m[1];
+ progressElement.value = parseInt(m[2])*100;
+ progressElement.max = parseInt(m[4])*100;
+ progressElement.hidden = false;
+ } else {
+ progressElement.value = null;
+ progressElement.max = null;
+ progressElement.hidden = true;
+ }
+ statusElement.innerHTML = text;
+ },
+ totalDependencies: 0,
+ monitorRunDependencies: function(left) {
+ this.totalDependencies = Math.max(this.totalDependencies, left);
+ Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
+ }
+ };
+ Module.setStatus('Downloading...');
+ </script>
+ <script type='text/javascript'>
+
+ {{{ SCRIPT_CODE }}}
+
+ </script>
+ </body>
+</html>
diff --git a/tests/gl_stride.c b/tests/gl_stride.c
new file mode 100644
index 00000000..c254ad5a
--- /dev/null
+++ b/tests/gl_stride.c
@@ -0,0 +1,152 @@
+/*******************************************************************
+ * *
+ * Using SDL With OpenGL *
+ * *
+ * Tutorial by Kyle Foley (sdw) *
+ * *
+ * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL *
+ * *
+ *******************************************************************/
+
+/*
+THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION
+AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN.
+
+THE ORIGINAL AUTHOR IS KYLE FOLEY.
+
+THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY
+OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF
+MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE,
+ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE
+RESULTING FROM THE USE, MODIFICATION, OR
+REDISTRIBUTION OF THIS SOFTWARE.
+*/
+
+#if !EMSCRIPTEN
+#define USE_GLEW 0
+#endif
+
+#if USE_GLEW
+#include "GL/glew.h"
+#endif
+
+#include "SDL/SDL.h"
+#include "SDL/SDL_image.h"
+#if !USE_GLEW
+#include "SDL/SDL_opengl.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ // Slightly different SDL initialization
+ if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) {
+ printf("Unable to initialize SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
+
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed*
+ if ( !screen ) {
+ printf("Unable to set video mode: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // Set the OpenGL state after creating the context with SDL_SetVideoMode
+
+ glClearColor( 0, 0, 0, 0 );
+
+#if !EMSCRIPTEN
+ glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL
+#endif
+
+ glViewport( 0, 0, 640, 480 );
+
+ glMatrixMode( GL_MODELVIEW );
+ glLoadIdentity();
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ typedef struct Vertex {
+ GLfloat x;
+ GLfloat y;
+ } Vertex;
+
+ typedef struct Color {
+ GLubyte r;
+ GLubyte g;
+ GLubyte b;
+ GLubyte a;
+ } Color;
+
+ Vertex vertices[3] = {
+ {-1.0, 0.0},
+ { 0.0, 1.0},
+ { 1.0, 0.0}
+ };
+
+ Color colors[3] = {
+ {0xFF, 0x00, 0x00, 0xFF},
+ {0x00, 0xFF, 0x00, 0xFF},
+ {0x00, 0x00, 0xFF, 0xFF}
+ };
+
+ Vertex vertices2[3] = {
+ {-1.0, 0.0},
+ { 1.0, 0.0},
+ { 0.0, -1.0}
+ };
+
+ Color colors2[3] = {
+ {0xFF, 0x00, 0x00, 0xFF},
+ {0x00, 0x00, 0xFF, 0xFF},
+ {0x00, 0xFF, 0x00, 0xFF}
+ };
+
+ // DRAW
+
+ // Clear the screen before drawing
+ glClear( GL_COLOR_BUFFER_BIT );
+
+ // This test ensures that we can use two separate arrays in memory for different
+ // attributes, and that they each can have different stride.
+ // The first test shows implicit striding (the zero indicates tightly packed)
+ // The second test shows explicit striding where the stride is passed in
+ // even though it also is tightly packed
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ // TEST 1
+
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
+
+ // TEST 2
+
+ glVertexPointer(2, GL_FLOAT, 8, vertices2);
+ glColorPointer(4, GL_UNSIGNED_BYTE, 4, colors2);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ SDL_GL_SwapBuffers();
+
+#if !EMSCRIPTEN
+ // Wait for 3 seconds to give us a chance to see the image
+ SDL_Delay(3000);
+#endif
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/tests/gl_stride.png b/tests/gl_stride.png
new file mode 100644
index 00000000..db1bc751
--- /dev/null
+++ b/tests/gl_stride.png
Binary files differ
diff --git a/tests/runner.py b/tests/runner.py
index db5ba108..aa2e1dc1 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -455,7 +455,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv) and 'brows
if len(sys.argv) == 2 and 'ALL.' in sys.argv[1]:
ignore, test = sys.argv[1].split('.')
print 'Running all test modes on test "%s"' % test
- sys.argv = [sys.argv[0], 'default.'+test, 'o1.'+test, 'o2.'+test, 'asm2.'+test, 'asm2g.'+test, 's_0_0.'+test, 's_0_1.'+test, 's_1_0.'+test, 's_1_1.'+test]
+ sys.argv = [sys.argv[0], 'default.'+test, 'o1.'+test, 'o2.'+test, 'asm1.'+test, 'asm2.'+test, 'asm2g.'+test, 's_0_0.'+test, 's_0_1.'+test, 's_1_0.'+test, 's_1_1.'+test]
class T(RunnerCore): # Short name, to make it more fun to use manually on the commandline
## Does a complete test - builds, runs, checks output, etc.
@@ -2310,8 +2310,6 @@ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too'
self.do_run(src, 'Assertion failed: 1 == false')
def test_longjmp(self):
- if Settings.ASM_JS: return self.skip('asm does not support longjmp')
-
src = r'''
#include <stdio.h>
#include <setjmp.h>
@@ -2343,8 +2341,6 @@ cat |umber one top notchfi FI FO FUM WHEN WHERE WHY HOW WHO|''', ['wowie', 'too'
self.do_run(src, 'second\nmain: 1\n')
def test_longjmp2(self):
- if Settings.ASM_JS: return self.skip('asm does not support longjmp')
-
src = r'''
#include <setjmp.h>
#include <stdio.h>
@@ -2391,8 +2387,6 @@ Exiting stack_manipulate_func, level: 0
''')
def test_longjmp3(self):
- if Settings.ASM_JS: return self.skip('asm does not support longjmp')
-
src = r'''
#include <setjmp.h>
#include <stdio.h>
@@ -2445,8 +2439,6 @@ Exiting setjmp function, level: 0
''')
def test_longjmp4(self):
- if Settings.ASM_JS: return self.skip('asm does not support longjmp')
-
src = r'''
#include <setjmp.h>
#include <stdio.h>
@@ -3541,7 +3533,7 @@ def process(filename):
double get() {
double ret = 0;
- __asm __volatile__("12/3.3":"=a"(ret));
+ __asm __volatile__("12/3.3":"=r"(ret));
return ret;
}
@@ -3709,6 +3701,7 @@ def process(filename):
def test_bigswitch(self):
if Settings.RELOOP: return self.skip('TODO: switch in relooper, issue #781')
+ if Settings.ASM_JS: return self.skip('TODO: switch too large for asm')
src = open(path_from_root('tests', 'bigswitch.cpp')).read()
self.do_run(src, '''34962: GL_ARRAY_BUFFER (0x8892)
@@ -9001,6 +8994,7 @@ TT = %s
exec('o2 = make_run("o2", compiler=CLANG, emcc_args=["-O2", "-s", "JS_CHUNK_SIZE=1024"])')
# asm.js
+ exec('asm1 = make_run("asm1", compiler=CLANG, emcc_args=["-O1", "-s", "ASM_JS=1"])')
exec('asm2 = make_run("asm2", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1"])')
exec('asm2g = make_run("asm2g", compiler=CLANG, emcc_args=["-O2", "-s", "ASM_JS=1", "-g", "-s", "ASSERTIONS=1", "--memory-init-file", "1"])')
@@ -10241,8 +10235,8 @@ f.close()
for args, fail in [
([], True), # without --bind, we fail
(['--bind'], False),
- (['--bind', '-O1'], False)
- # XXX TODO (['--bind', '-O2'], False)
+ (['--bind', '-O1'], False),
+ (['--bind', '-O2'], False)
]:
print args, fail
self.clear()
@@ -11586,6 +11580,9 @@ elif 'browser' in str(sys.argv):
def test_gl_renderers(self):
self.btest('gl_renderers.c', reference='gl_renderers.png', args=['-s', 'GL_UNSAFE_OPTS=0'])
+ def test_gl_stride(self):
+ self.btest('gl_stride.c', reference='gl_stride.png', args=['-s', 'GL_UNSAFE_OPTS=0'])
+
def test_matrix_identity(self):
self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'])
@@ -12102,8 +12099,7 @@ elif 'benchmark' in str(sys.argv):
'-o', final_filename] + shared_args + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate()
assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0]
- if self.save_JS:
- self.hardcode_arguments(final_filename, args)
+ self.hardcode_arguments(final_filename, args)
# Run JS
global total_times, tests_done
diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js
index 4cf15c62..e477c320 100644
--- a/tools/eliminator/asm-eliminator-test-output.js
+++ b/tools/eliminator/asm-eliminator-test-output.js
@@ -108,4 +108,19 @@ function label() {
i();
}
}
+function switchy() {
+ var no = 0, yes = 0;
+ while (1) switch (label | 0) {
+ case x:
+ no = 100;
+ break;
+ case y:
+ yes = 111;
+ yes = yes * 2;
+ print(yes);
+ yes--;
+ print(yes / 2);
+ continue;
+ }
+}
diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js
index d2c0507c..acc07edb 100644
--- a/tools/eliminator/asm-eliminator-test.js
+++ b/tools/eliminator/asm-eliminator-test.js
@@ -141,5 +141,20 @@ function label() {
i();
}
}
+function switchy() {
+ var no = 0, yes = 0;
+ while (1) switch (label | 0) {
+ case x:
+ no = 100; // eliminatable in theory, but eliminator does not look into switch. must leave def above as well.
+ break;
+ case y:
+ yes = 111;
+ yes = yes*2;
+ print(yes);
+ yes--;
+ print(yes/2);
+ continue;
+ }
+}
// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label"]
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 5ede0ce8..598287db 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -1765,6 +1765,7 @@ function eliminate(ast, memSafe) {
var values = {};
var locals = {};
var varsToRemove = {}; // variables being removed, that we can eliminate all 'var x;' of (this refers to 'var' nodes we should remove)
+ // 1 means we should remove it, 2 means we successfully removed it
var varsToTryToRemove = {}; // variables that have 0 uses, but have side effects - when we scan we can try to remove them
// add arguments as locals
if (func[2]) {
@@ -1822,7 +1823,7 @@ function eliminate(ast, memSafe) {
});
}
if (!hasSideEffects) {
- varsToRemove[name] = 1; // remove it normally
+ varsToRemove[name] = !definitions[name] ? 2 : 1; // remove it normally
sideEffectFree[name] = true;
} else {
varsToTryToRemove[name] = 1; // try to remove it later during scanning
@@ -1979,7 +1980,7 @@ function eliminate(ast, memSafe) {
for (var i = 0; i < value.length; i++) {
node[i] = value[i];
}
- varsToRemove[name] = 1;
+ varsToRemove[name] = 2;
}
} else {
if (allowTracking) track(name, node[3], node);
@@ -2019,7 +2020,7 @@ function eliminate(ast, memSafe) {
for (var i = 0; i < value.length; i++) {
node[i] = value[i];
}
- varsToRemove[name] = 1;
+ varsToRemove[name] = 2;
}
}
}
@@ -2123,7 +2124,7 @@ function eliminate(ast, memSafe) {
function doEliminate(name, node) {
//printErr('elim!!!!! ' + name);
// yes, eliminate!
- varsToRemove[name] = 1; // both assign and var definitions can have other vars we must clean up
+ varsToRemove[name] = 2; // both assign and var definitions can have other vars we must clean up
var info = tracked[name];
delete tracked[name];
var defNode = info.defNode;
@@ -2181,7 +2182,7 @@ function eliminate(ast, memSafe) {
//printErr('cleaning up ' + JSON.stringify(varsToRemove));
traverse(func, function(node, type) {
if (type === 'var') {
- node[1] = node[1].filter(function(pair) { return !(pair[0] in varsToRemove) });
+ node[1] = node[1].filter(function(pair) { return !varsToRemove[pair[0]] });
if (node[1].length == 0) {
// wipe out an empty |var;|
node[0] = 'toplevel';
@@ -2192,7 +2193,7 @@ function eliminate(ast, memSafe) {
if (asm) {
for (var v in varsToRemove) {
- delete asmData.vars[v];
+ if (varsToRemove[v] == 2) delete asmData.vars[v];
}
denormalizeAsm(func, asmData);
}
diff --git a/tools/shared.py b/tools/shared.py
index a53c0a7b..eedb2e8b 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -729,13 +729,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e
basename = os.path.basename(f)
cache[cache_name].append((basename, open(f, 'rb').read()))
break
- except:
+ except Exception, e:
if i > 0:
# Due to the ugly hack above our best guess is to output the first run
with open_make_err(0) as ferr:
for line in ferr:
sys.stderr.write(line)
- raise Exception('could not build library ' + name)
+ raise Exception('could not build library ' + name + ' due to exception ' + str(e))
if old_dir:
os.chdir(old_dir)
return generated_libs