aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/embind/embind.js87
-rwxr-xr-xtests/embind/embind.test.js56
-rw-r--r--tests/embind/embind_test.cpp93
3 files changed, 227 insertions, 9 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index df581298..f48bcf7b 100755
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -1060,14 +1060,40 @@ function __embind_register_class_function(
classType = classType[0];
var humanName = classType.name + '.' + methodName;
- classType.registeredClass.instancePrototype[methodName] = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
+ var unboundTypesHandler = function() {
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ };
+
+ var method = classType.registeredClass.instancePrototype[methodName];
+ if (undefined === method || (undefined === method.overloadTable && method.className !== classType.name && method.argCount === argCount-2)) {
+ // This is the first overload to be registered, OR we are replacing a function in the base class with a function in the derived class.
+ classType.registeredClass.instancePrototype[methodName] = unboundTypesHandler;
+ classType.registeredClass.instancePrototype[methodName].argCount = argCount-2;
+ classType.registeredClass.instancePrototype[methodName].className = classType.name;
+ } else {
+ // There was an existing function with the same name registered. Set up a function overload routing table.
+ if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) {
+ var prevFunc = classType.registeredClass.instancePrototype[methodName];
+
+ // Inject an overload resolver function that routes to the appropriate overload based on the number of arguments.
+ classType.registeredClass.instancePrototype[methodName] = function() {
+ // TODO This check can be removed in -O3 level "unsafe" optimizations.
+ if (!classType.registeredClass.instancePrototype[methodName].overloadTable.hasOwnProperty(arguments.length)) {
+ throwBindingError("Member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.instancePrototype[methodName].overloadTable + ")!");
+ }
+ return classType.registeredClass.instancePrototype[methodName].overloadTable[arguments.length].apply(this, arguments);
+ };
+ // Move the previous function into the overload table.
+ classType.registeredClass.instancePrototype[methodName].overloadTable = [];
+ classType.registeredClass.instancePrototype[methodName].overloadTable[prevFunc.argCount] = prevFunc;
+ }
+ classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = unboundTypesHandler;
+ }
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- classType.registeredClass.instancePrototype[methodName] = createNamedFunction(makeLegalFunctionName(humanName), function() {
+ var memberFunction = createNamedFunction(makeLegalFunctionName(humanName), function() {
if (arguments.length !== argCount - 2) {
- throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
+ throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-2));
}
validateThis(this, classType, humanName);
@@ -1084,6 +1110,15 @@ function __embind_register_class_function(
runDestructors(destructors);
return rv;
});
+
+ // 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.
+ if (undefined === classType.registeredClass.instancePrototype[methodName].overloadTable) {
+ classType.registeredClass.instancePrototype[methodName] = memberFunction;
+ } else {
+ classType.registeredClass.instancePrototype[methodName].overloadTable[argCount-2] = memberFunction;
+ }
+
return [];
});
return [];
@@ -1105,12 +1140,46 @@ function __embind_register_class_class_function(
classType = classType[0];
var humanName = classType.name + '.' + methodName;
- classType.registeredClass.constructor[methodName] = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
+ var unboundTypesHandler = function() {
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ };
+
+ if (undefined === classType.registeredClass.constructor[methodName]) {
+ // This is the first function to be registered with this name.
+ classType.registeredClass.constructor[methodName] = unboundTypesHandler;
+ classType.registeredClass.constructor[methodName].argCount = argCount-1;
+ } else {
+ // There was an existing function to be registered with this name. Set up an overload table to
+ // resolve between them.
+ if (undefined === classType.registeredClass.constructor[methodName].overloadTable) {
+ var prevFunc = classType.registeredClass.constructor[methodName];
+
+ // Inject an overload handler function that resolves the proper function to call based on the
+ // number of parameters passed to the function.
+ classType.registeredClass.constructor[methodName] = function() {
+ // TODO This check can be removed in -O3 level "unsafe" optimizations.
+ if (!classType.registeredClass.constructor[methodName].overloadTable.hasOwnProperty(arguments.length)) {
+ throwBindingError("Static member function '" + humanName + "' called with an invalid number of arguments (" + arguments.length + ") - expects one of (" + classType.registeredClass.constructor[methodName].overloadTable + ")!");
+ }
+ return classType.registeredClass.constructor[methodName].overloadTable[arguments.length].apply(this, arguments);
+ };
+
+ classType.registeredClass.constructor[methodName].overloadTable = [];
+ // Move the old function into the overload table.
+ classType.registeredClass.constructor[methodName].overloadTable[prevFunc.argCount] = prevFunc;
+ }
+ classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = unboundTypesHandler;
+ }
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
+ // 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);
+ if (undefined === classType.registeredClass.constructor[methodName].overloadTable) {
+ classType.registeredClass.constructor[methodName] = func;
+ } else {
+ classType.registeredClass.constructor[methodName].overloadTable[argCount-1] = func;
+ }
return [];
});
return [];
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 11746214..020719d5 100755
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -620,6 +620,62 @@ module({
assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); });
});
+ test("overloading of class member functions", function() {
+ var foo = new cm.MultipleOverloads();
+ assert.equal(foo.Func(10), 1);
+ assert.equal(foo.WhichFuncCalled(), 1);
+ assert.equal(foo.Func(20, 20), 2);
+ assert.equal(foo.WhichFuncCalled(), 2);
+ foo.delete();
+ });
+
+ test("wrong number of arguments to an overloaded class member function", function() {
+ var foo = new cm.MultipleOverloads();
+ assert.throws(cm.BindingError, function() { foo.Func(); });
+ assert.throws(cm.BindingError, function() { foo.Func(30, 30, 30); });
+ foo.delete();
+ });
+
+ test("wrong number of arguments to an overloaded class static function", function() {
+ assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(); });
+ assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); });
+ });
+
+ test("overloading of derived class member functions", function() {
+ var foo = new cm.MultipleOverloadsDerived();
+
+ // NOTE: In C++, default lookup rules will hide overloads from base class if derived class creates them.
+ // In JS, we make the base class overloads implicitly available. In C++, they would need to be explicitly
+ // invoked, like foo.MultipleOverloads::Func(10);
+ assert.equal(foo.Func(10), 1);
+ assert.equal(foo.WhichFuncCalled(), 1);
+ assert.equal(foo.Func(20, 20), 2);
+ assert.equal(foo.WhichFuncCalled(), 2);
+
+ assert.equal(foo.Func(30, 30, 30), 3);
+ assert.equal(foo.WhichFuncCalled(), 3);
+ assert.equal(foo.Func(40, 40, 40, 40), 4);
+ assert.equal(foo.WhichFuncCalled(), 4);
+ foo.delete();
+ });
+
+ test("overloading of class static functions", function() {
+ assert.equal(cm.MultipleOverloads.StaticFunc(10), 1);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1);
+ assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2);
+ });
+
+ test("overloading of derived class static functions", function() {
+ assert.equal(cm.MultipleOverloadsDerived.StaticFunc(30, 30, 30), 3);
+ // TODO: Cannot access static member functions of a Base class via Derived.
+// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 3);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3);
+ assert.equal(cm.MultipleOverloadsDerived.StaticFunc(40, 40, 40, 40), 4);
+ // TODO: Cannot access static member functions of a Base class via Derived.
+// assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 4);
+ assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 4);
+ });
/*
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 be089f9f..1ef3fb16 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1339,6 +1339,83 @@ public:
}
};
+class MultipleOverloads {
+public:
+ MultipleOverloads() {}
+
+ int value;
+ static int staticValue;
+
+ int Func(int i) {
+ assert(i == 10);
+ value = 1;
+ return 1;
+ }
+ int Func(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ value = 2;
+ return 2;
+ }
+
+ int WhichFuncCalled() const {
+ return value;
+ }
+
+ static int StaticFunc(int i) {
+ assert(i == 10);
+ staticValue = 1;
+ return 1;
+ }
+ static int StaticFunc(int i, int j) {
+ assert(i == 20);
+ assert(j == 20);
+ staticValue = 2;
+ return 2;
+ }
+
+ static int WhichStaticFuncCalled() {
+ return staticValue;
+ }
+};
+
+class MultipleOverloadsDerived : public MultipleOverloads {
+public:
+ MultipleOverloadsDerived() {}
+
+ int Func(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ value = 3;
+ return 3;
+ }
+ int Func(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ value = 4;
+ return 4;
+ }
+
+ static int StaticFunc(int i, int j, int k) {
+ assert(i == 30);
+ assert(j == 30);
+ assert(k == 30);
+ staticValue = 3;
+ return 3;
+ }
+ static int StaticFunc(int i, int j, int k, int l) {
+ assert(i == 40);
+ assert(j == 40);
+ assert(k == 40);
+ assert(l == 40);
+ staticValue = 4;
+ return 4;
+ }
+};
+
int overloaded_function(int i)
{
assert(i == 10);
@@ -1823,6 +1900,22 @@ EMSCRIPTEN_BINDINGS(tests) {
.constructor<int, int>()
.constructor<int, int, int>()
.function("WhichCtorCalled", &MultipleCtors::WhichCtorCalled);
+
+ class_<MultipleOverloads>("MultipleOverloads")
+ .constructor<>()
+ .function("Func", (int(MultipleOverloads::*)(int))&MultipleOverloads::Func)
+ .function("Func", (int(MultipleOverloads::*)(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_<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);
}
// tests for out-of-order registration