aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Austin <chad@imvu.com>2013-03-20 18:39:45 -0700
committerJukka Jylänki <jujjyl@gmail.com>2013-04-12 14:26:30 +0300
commit0a6f5476f19792164e122c27ffca83baa69c1ca3 (patch)
tree25131d3227b4bdf2c2b58e2a7ce4f9775f35423f
parent4d6377d15e3ba48f4a6d5eb311bbd8afb618cf0d (diff)
If calling function that uses unbound types, give a sensible error message.
-rwxr-xr-xsrc/embind/embind.js214
-rwxr-xr-xsystem/lib/embind/bind.cpp12
2 files changed, 141 insertions, 85 deletions
diff --git a/src/embind/embind.js b/src/embind/embind.js
index 72312af1..c27c49b2 100755
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -8,12 +8,34 @@ var InternalError = Module.InternalError = extendError(Error, 'InternalError');
var BindingError = Module.BindingError = extendError(Error, 'BindingError');
var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError');
-function throwInternalError(value) {
- throw new InternalError(value);
+function throwInternalError(message) {
+ throw new InternalError(message);
}
-function throwBindingError(value) {
- throw new BindingError(value);
+function throwBindingError(message) {
+ throw new BindingError(message);
+}
+
+function throwUnboundTypeError(message, types) {
+ var unboundTypes = [];
+ var seen = {};
+ function visit(type) {
+ if (seen[type]) {
+ return;
+ }
+ if (registeredTypes[type]) {
+ return;
+ }
+ if (typeDependencies[type]) {
+ typeDependencies[type].forEach(visit);
+ return;
+ }
+ unboundTypes.push(type);
+ seen[type] = true;
+ }
+ types.forEach(visit);
+
+ throw new UnboundTypeError(message + ': ' + unboundTypes.map(getTypeName).join([', ']));
}
function exposePublicSymbol(name, value) {
@@ -75,6 +97,9 @@ var registeredTypes = {};
// typeID -> [callback]
var awaitingDependencies = {};
+// typeID -> [dependentTypes]
+var typeDependencies = {};
+
// class typeID -> {pointerType: ..., constPointerType: ...}
var registeredPointers = {};
@@ -88,6 +113,7 @@ function registerType(rawType, registeredInstance) {
}
registeredTypes[rawType] = registeredInstance;
+ delete typeDependencies[rawType];
if (awaitingDependencies.hasOwnProperty(rawType)) {
var callbacks = awaitingDependencies[rawType];
@@ -98,7 +124,21 @@ function registerType(rawType, registeredInstance) {
}
}
-function whenDependentTypesAreResolved(dependentTypes, onComplete) {
+function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverters) {
+ myTypes.forEach(function(type) {
+ typeDependencies[type] = dependentTypes;
+ });
+
+ function onComplete(typeConverters) {
+ var myTypeConverters = getTypeConverters(typeConverters);
+ if (myTypeConverters.length !== myTypes.length) {
+ throwInternalError('Mismatched type converter count');
+ }
+ for (var i = 0; i < myTypes.length; ++i) {
+ registerType(myTypes[i], myTypeConverters[i]);
+ }
+ }
+
var typeConverters = new Array(dependentTypes.length);
var unregisteredTypes = [];
var registered = 0;
@@ -278,15 +318,16 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
rawInvoker = FUNCTION_TABLE[rawInvoker];
var invoker = function() {
- throw new UnboundTypeError('Cannot call ' + name + ' due to unbound types: UnboundFoo');
+ throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
};
exposePublicSymbol(name, function() {
return invoker.apply(this, arguments);
});
- whenDependentTypesAreResolved(argTypes, function(argTypes) {
+ whenDependentTypesAreResolved([], argTypes, function(argTypes) {
invoker = makeInvoker(name, argCount, argTypes, rawInvoker, fn);
+ return [];
});
}
@@ -349,7 +390,7 @@ function __embind_register_tuple_element(
tupleType.elements.push(undefined);
// TODO: test incomplete registration of value tuples
- whenDependentTypesAreResolved([rawType], function(type) {
+ whenDependentTypesAreResolved([], [rawType], function(type) {
type = type[0];
tupleType.elements[index] = {
read: function(ptr) {
@@ -361,6 +402,7 @@ function __embind_register_tuple_element(
runDestructors(destructors);
}
};
+ return [];
});
}
@@ -380,7 +422,7 @@ function __embind_register_tuple_element_accessor(
rawStaticSetter = FUNCTION_TABLE[rawStaticSetter];
setter = copyMemberPointer(setter, setterSize);
- whenDependentTypesAreResolved([rawElementType], function(elementType) {
+ whenDependentTypesAreResolved([], [rawElementType], function(elementType) {
elementType = elementType[0];
tupleType.elements.push({
read: function(ptr) {
@@ -395,6 +437,7 @@ function __embind_register_tuple_element_accessor(
runDestructors(destructors);
}
});
+ return [];
});
}
@@ -454,7 +497,7 @@ function __embind_register_struct_field(
rawSetter = FUNCTION_TABLE[rawSetter];
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
// TODO: test incomplete registration of value structs
- whenDependentTypesAreResolved([rawFieldType], function(fieldType) {
+ whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) {
fieldType = fieldType[0];
structType.fields[fieldName] = {
read: function(ptr) {
@@ -466,6 +509,7 @@ function __embind_register_struct_field(
runDestructors(destructors);
}
};
+ return [];
});
}
@@ -756,75 +800,79 @@ function __embind_register_class(
downcast = FUNCTION_TABLE[downcast];
var legalFunctionName = makeLegalFunctionName(name);
- whenDependentTypesAreResolved(baseClassRawType ? [baseClassRawType] : [], function(base) {
- base = base[0];
-
- var baseClass;
- var basePrototype;
- if (baseClassRawType) {
- baseClass = base.registeredClass;
- basePrototype = baseClass.instancePrototype;
- } else {
- basePrototype = ClassHandle.prototype;
- }
-
- var constructor = createNamedFunction(legalFunctionName, function() {
- if (Object.getPrototypeOf(this) !== instancePrototype) {
- throw new BindingError("Use 'new' to construct " + name);
- }
- var body = registeredClass.constructor_body;
- if (undefined === body) {
- throw new BindingError(name + " has no accessible constructor");
+ whenDependentTypesAreResolved(
+ [rawType, rawPointerType, rawConstPointerType],
+ baseClassRawType ? [baseClassRawType] : [],
+ function(base) {
+ base = base[0];
+
+ var baseClass;
+ var basePrototype;
+ if (baseClassRawType) {
+ baseClass = base.registeredClass;
+ basePrototype = baseClass.instancePrototype;
+ } else {
+ basePrototype = ClassHandle.prototype;
}
- return body.apply(this, arguments);
- });
- var instancePrototype = Object.create(basePrototype, {
- constructor: { value: constructor },
- });
-
- constructor.prototype = instancePrototype;
-
- var registeredClass = new RegisteredClass(
- name,
- constructor,
- instancePrototype,
- rawDestructor,
- baseClass,
- getActualType,
- upcast,
- downcast);
-
- registerType(rawType, new RegisteredPointer(
- name,
- registeredClass,
- true,
- false,
- false));
-
- var pointerType = new RegisteredPointer(
- name + '*',
- registeredClass,
- false,
- false,
- false);
- registerType(rawPointerType, pointerType);
-
- var constPointerType = new RegisteredPointer(
- name + ' const*',
- registeredClass,
- false,
- true,
- false);
- registerType(rawConstPointerType, constPointerType);
+ var constructor = createNamedFunction(legalFunctionName, function() {
+ if (Object.getPrototypeOf(this) !== instancePrototype) {
+ throw new BindingError("Use 'new' to construct " + name);
+ }
+ var body = registeredClass.constructor_body;
+ if (undefined === body) {
+ throw new BindingError(name + " has no accessible constructor");
+ }
+ return body.apply(this, arguments);
+ });
- registeredPointers[rawType] = {
- pointerType: pointerType,
- constPointerType: constPointerType
- };
+ var instancePrototype = Object.create(basePrototype, {
+ constructor: { value: constructor },
+ });
- exposePublicSymbol(legalFunctionName, constructor);
- });
+ constructor.prototype = instancePrototype;
+
+ var registeredClass = new RegisteredClass(
+ name,
+ constructor,
+ instancePrototype,
+ rawDestructor,
+ baseClass,
+ getActualType,
+ upcast,
+ downcast);
+
+ var referenceConverter = new RegisteredPointer(
+ name,
+ registeredClass,
+ true,
+ false,
+ false);
+
+ var pointerConverter = new RegisteredPointer(
+ name + '*',
+ registeredClass,
+ false,
+ false,
+ false);
+
+ var constPointerConverter = new RegisteredPointer(
+ name + ' const*',
+ registeredClass,
+ false,
+ true,
+ false);
+
+ registeredPointers[rawType] = {
+ pointerType: pointerConverter,
+ constPointerType: constPointerConverter
+ };
+
+ exposePublicSymbol(legalFunctionName, constructor);
+
+ return [referenceConverter, pointerConverter, constPointerConverter];
+ }
+ );
}
function __embind_register_class_constructor(
@@ -837,7 +885,7 @@ function __embind_register_class_constructor(
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
invoker = FUNCTION_TABLE[invoker];
- whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) {
+ whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) {
var classType = argTypes[0];
argTypes = argTypes.slice(1);
var humanName = 'constructor ' + classType.name;
@@ -857,6 +905,7 @@ function __embind_register_class_constructor(
return argTypes[0].fromWireType(ptr);
};
+ return [];
});
}
@@ -912,7 +961,7 @@ function __embind_register_class_function(
rawInvoker = FUNCTION_TABLE[rawInvoker];
memberFunction = copyMemberPointer(memberFunction, memberFunctionSize);
- whenDependentTypesAreResolved([rawClassType].concat(rawArgTypes), function(argTypes) {
+ whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) {
var classType = argTypes[0];
argTypes = argTypes.slice(1);
var humanName = classType.name + '.' + methodName;
@@ -938,6 +987,7 @@ function __embind_register_class_function(
runDestructors(destructors);
return rv;
};
+ return [];
});
}
@@ -953,9 +1003,10 @@ function __embind_register_class_class_function(
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
methodName = Pointer_stringify(methodName);
rawInvoker = FUNCTION_TABLE[rawInvoker];
- whenDependentTypesAreResolved(rawArgTypes, function(argTypes) {
+ whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
var humanName = classType.name + '.' + methodName;
classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
+ return [];
});
}
@@ -972,7 +1023,7 @@ function __embind_register_class_property(
getter = FUNCTION_TABLE[getter];
setter = FUNCTION_TABLE[setter];
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
- whenDependentTypesAreResolved([rawClassType, rawFieldType], function(converters) {
+ whenDependentTypesAreResolved([], [rawClassType, rawFieldType], function(converters) {
var classType = converters[0];
var fieldType = converters[1];
var humanName = classType.name + '.' + fieldName;
@@ -989,6 +1040,7 @@ function __embind_register_class_property(
},
enumerable: true
});
+ return [];
});
}
@@ -1020,7 +1072,7 @@ function __embind_register_smart_ptr(
rawShare = FUNCTION_TABLE[rawShare];
rawDestructor = FUNCTION_TABLE[rawDestructor];
- whenDependentTypesAreResolved([rawPointeeType], function(pointeeType) {
+ whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) {
pointeeType = pointeeType[0];
var registeredPointer = new RegisteredPointer(
@@ -1036,7 +1088,7 @@ function __embind_register_smart_ptr(
rawConstructor,
rawShare,
rawDestructor);
- registerType(rawType, registeredPointer);
+ return [registeredPointer];
});
}
diff --git a/system/lib/embind/bind.cpp b/system/lib/embind/bind.cpp
index 04a30dfc..a75ba96a 100755
--- a/system/lib/embind/bind.cpp
+++ b/system/lib/embind/bind.cpp
@@ -12,6 +12,12 @@ static std::string _embind_getTypeName(intptr_t ti_raw) {
auto ti = reinterpret_cast<const std::type_info*>(ti_raw);
int stat;
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
+ if (stat == 0) {
+ std::string rv(demangled);
+ free(demangled);
+ return rv;
+ }
+
switch (stat) {
case -1:
return "<allocation failure>";
@@ -19,11 +25,9 @@ static std::string _embind_getTypeName(intptr_t ti_raw) {
return "<invalid C++ symbol>";
case -3:
return "<invalid argument>";
+ default:
+ return "<unknown error>";
}
-
- std::string rv(demangled);
- free(demangled);
- return rv;
}
namespace emscripten {