aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/embind/embind.js68
-rwxr-xr-xtests/embind/embind.test.js12
-rw-r--r--tests/embind/embind_test.cpp16
3 files changed, 89 insertions, 7 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index c298d8ba..e050d3c2 100755
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -39,18 +39,72 @@ function throwUnboundTypeError(message, types) {
throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', ']));
}
-function exposePublicSymbol(name, value) {
+/* Registers a symbol (function, class, enum, ...) as part of the Module JS object so that
+ hand-written code is able to access that symbol via 'Module.name'.
+ name: The name of the symbol that's being exposed.
+ value: The object itself to expose (function, class, ...)
+ numArguments: For functions, specifies the number of arguments the function takes in. For other types, unused and undefined.
+
+ To implement support for multiple overloads of a function, an 'overload selector' function is used. That selector function chooses
+ the appropriate overload to call from an function overload table. This selector function is only used if multiple overloads are
+ actually registered, since it carries a slight performance penalty. */
+function exposePublicSymbol(name, value, numArguments) {
if (Module.hasOwnProperty(name)) {
- throwBindingError("Cannot register public name '" + name + "' twice");
+ if (undefined === numArguments || (undefined !== Module[name].overloadTable && undefined !== Module[name].overloadTable[numArguments])) {
+ throwBindingError("Cannot register public name '" + name + "' twice");
+ }
+
+ // We are exposing a function with the same name as an existing function. Create an overload table and a function selector
+ // that routes between the two.
+
+ // If we don't yet have an overload selector, install an overload selector that routes the function call to a table of overloads based on # of arguments to function.
+ if (undefined === Module[name].overloadTable) {
+ var prevFunc = Module[name];
+
+ // Inject an overload selector in place of the previous function.
+ Module[name] = function() {
+ // TODO This check can be removed in -O3 level "unsafe" optimizations.
+ if (!Module[name].overloadTable.hasOwnProperty(arguments.length)) {
+ throwBindingError("Function '" + name + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + Object.keys(Module[name].overloadTable) + ")!");
+ }
+ return Module[name].overloadTable[arguments.length].apply(this, arguments);
+ };
+ // An overloadTable maintains a registry of all function overloads.
+ Module[name].overloadTable = [];
+ // Move the old function into the overload table.
+ Module[name].overloadTable[prevFunc.numArguments] = prevFunc;
+ }
+
+ if (Module.hasOwnProperty(numArguments)) {
+ throwBindingError("Cannot register multiple overloads of a function with the same number of arguments (" + numArguments + ")!");
+ }
+ // Add the new function into the overload table.
+ Module[name].overloadTable[numArguments] = value;
+ }
+ else {
+ Module[name] = value;
+ if (undefined !== numArguments) {
+ Module[name].numArguments = numArguments;
+ }
}
- Module[name] = value;
}
-function replacePublicSymbol(name, value) {
+function replacePublicSymbol(name, value, numArguments) {
if (!Module.hasOwnProperty(name)) {
throwInternalError('Replacing nonexistant public symbol');
}
- Module[name] = value;
+ // If there's an overload table for this symbol, replace the symbol in the overload table instead.
+ if (undefined !== Module[name].overloadTable && undefined !== numArguments) {
+ Module[name].overloadTable[numArguments] = value;
+ }
+ else {
+ Module[name] = value;
+ /* XXX TODO unneeded?!
+ if (undefined !== numArguments) {
+ Module[name].numArguments = numArguments;
+ }
+ */
+ }
}
// from https://github.com/imvu/imvujs/blob/master/src/error.js
@@ -345,10 +399,10 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
exposePublicSymbol(name, function() {
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
- });
+ }, argCount - 1);
whenDependentTypesAreResolved([], argTypes, function(argTypes) {
- replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn));
+ replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn), argCount - 1);
return [];
});
}
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 539b837f..a577cd37 100755
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -608,6 +608,18 @@ module({
assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); });
});
+ test("overloading of free functions", function() {
+ var a = cm.overloaded_function(10);
+ assert.equal(a, 1);
+ var b = cm.overloaded_function(20, 20);
+ assert.equal(b, 2);
+ });
+
+ test("wrong number of arguments to an overloaded free function", function() {
+ assert.throws(cm.BindingError, function() { cm.overloaded_function(); });
+ assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); });
+ });
+
/*
test("can get templated member classes then call its member functions", function() {
var p = new cm.ContainsTemplatedMemberClass();
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index f23b9152..0ae1801a 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1339,6 +1339,19 @@ public:
}
};
+int overloaded_function(int i)
+{
+ assert(i == 10);
+ return 1;
+}
+
+int overloaded_function(int i, int j)
+{
+ assert(i == 20);
+ assert(j == 20);
+ return 2;
+}
+
EMSCRIPTEN_BINDINGS(tests) {
register_js_interface();
@@ -1785,6 +1798,9 @@ 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);
+
class_<MultipleCtors>("MultipleCtors")
.constructor<int>()
.constructor<int, int>()