Add sensible error messages when working with classes that depend on unbound types.
This commit is contained in:
Родитель
0a6f5476f1
Коммит
e10ede1e07
|
@ -3,6 +3,7 @@
|
|||
/*global FUNCTION_TABLE, HEAP32, HEAPU8*/
|
||||
/*global Pointer_stringify*/
|
||||
/*global __emval_register, _emval_handle_array, __emval_decref*/
|
||||
/*global ___getTypeName*/
|
||||
|
||||
var InternalError = Module.InternalError = extendError(Error, 'InternalError');
|
||||
var BindingError = Module.BindingError = extendError(Error, 'BindingError');
|
||||
|
@ -45,6 +46,13 @@ function exposePublicSymbol(name, value) {
|
|||
Module[name] = value;
|
||||
}
|
||||
|
||||
function replacePublicSymbol(name, value) {
|
||||
if (!Module.hasOwnProperty(name)) {
|
||||
throwInternalError('Replacing nonexistant public symbol');
|
||||
}
|
||||
Module[name] = value;
|
||||
}
|
||||
|
||||
// from https://github.com/imvu/imvujs/blob/master/src/error.js
|
||||
function extendError(baseErrorType, errorName) {
|
||||
var errorClass = createNamedFunction(errorName, function(message) {
|
||||
|
@ -165,7 +173,10 @@ function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverter
|
|||
}
|
||||
|
||||
function getTypeName(type) {
|
||||
return Module._embind_getTypeName(type);
|
||||
var ptr = ___getTypeName(type);
|
||||
var rv = Pointer_stringify(ptr);
|
||||
_free(ptr);
|
||||
return rv;
|
||||
}
|
||||
|
||||
function heap32VectorToArray(count, firstElement) {
|
||||
|
@ -317,16 +328,12 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
|
|||
name = Pointer_stringify(name);
|
||||
rawInvoker = FUNCTION_TABLE[rawInvoker];
|
||||
|
||||
var invoker = function() {
|
||||
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
|
||||
};
|
||||
|
||||
exposePublicSymbol(name, function() {
|
||||
return invoker.apply(this, arguments);
|
||||
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes);
|
||||
});
|
||||
|
||||
whenDependentTypesAreResolved([], argTypes, function(argTypes) {
|
||||
invoker = makeInvoker(name, argCount, argTypes, rawInvoker, fn);
|
||||
replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn));
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
@ -800,6 +807,11 @@ function __embind_register_class(
|
|||
downcast = FUNCTION_TABLE[downcast];
|
||||
var legalFunctionName = makeLegalFunctionName(name);
|
||||
|
||||
exposePublicSymbol(legalFunctionName, function() {
|
||||
// this code cannot run if baseClassRawType is zero
|
||||
throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]);
|
||||
});
|
||||
|
||||
whenDependentTypesAreResolved(
|
||||
[rawType, rawPointerType, rawConstPointerType],
|
||||
baseClassRawType ? [baseClassRawType] : [],
|
||||
|
@ -868,7 +880,7 @@ function __embind_register_class(
|
|||
constPointerType: constPointerConverter
|
||||
};
|
||||
|
||||
exposePublicSymbol(legalFunctionName, constructor);
|
||||
replacePublicSymbol(legalFunctionName, constructor);
|
||||
|
||||
return [referenceConverter, pointerConverter, constPointerConverter];
|
||||
}
|
||||
|
@ -885,26 +897,33 @@ function __embind_register_class_constructor(
|
|||
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
|
||||
invoker = FUNCTION_TABLE[invoker];
|
||||
|
||||
whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) {
|
||||
var classType = argTypes[0];
|
||||
argTypes = argTypes.slice(1);
|
||||
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
|
||||
classType = classType[0];
|
||||
var humanName = 'constructor ' + classType.name;
|
||||
|
||||
classType.registeredClass.constructor_body = function() {
|
||||
if (arguments.length !== argCount - 1) {
|
||||
throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
|
||||
}
|
||||
var destructors = [];
|
||||
var args = new Array(argCount);
|
||||
args[0] = rawConstructor;
|
||||
for (var i = 1; i < argCount; ++i) {
|
||||
args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
|
||||
}
|
||||
|
||||
var ptr = invoker.apply(null, args);
|
||||
runDestructors(destructors);
|
||||
|
||||
return argTypes[0].fromWireType(ptr);
|
||||
throwUnboundTypeError('Cannot construct ' + classType.name + ' due to unbound types', rawArgTypes);
|
||||
};
|
||||
|
||||
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
|
||||
classType.registeredClass.constructor_body = function() {
|
||||
if (arguments.length !== argCount - 1) {
|
||||
throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
|
||||
}
|
||||
var destructors = [];
|
||||
var args = new Array(argCount);
|
||||
args[0] = rawConstructor;
|
||||
for (var i = 1; i < argCount; ++i) {
|
||||
args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
|
||||
}
|
||||
|
||||
var ptr = invoker.apply(null, args);
|
||||
runDestructors(destructors);
|
||||
|
||||
return argTypes[0].fromWireType(ptr);
|
||||
};
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
@ -961,32 +980,39 @@ function __embind_register_class_function(
|
|||
rawInvoker = FUNCTION_TABLE[rawInvoker];
|
||||
memberFunction = copyMemberPointer(memberFunction, memberFunctionSize);
|
||||
|
||||
whenDependentTypesAreResolved([], [rawClassType].concat(rawArgTypes), function(argTypes) {
|
||||
var classType = argTypes[0];
|
||||
argTypes = argTypes.slice(1);
|
||||
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
|
||||
classType = classType[0];
|
||||
var humanName = classType.name + '.' + methodName;
|
||||
|
||||
classType.registeredClass.instancePrototype[methodName] = function() {
|
||||
if (arguments.length !== argCount - 1) {
|
||||
throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
|
||||
}
|
||||
|
||||
var ptr = validateThis(this, classType, humanName);
|
||||
if (!isConst && this.$$.ptrType.isConst) {
|
||||
throwBindingError('Cannot call non-const method ' + humanName + ' on const reference');
|
||||
}
|
||||
|
||||
var destructors = [];
|
||||
var args = new Array(argCount + 1);
|
||||
args[0] = ptr;
|
||||
args[1] = memberFunction;
|
||||
for (var i = 1; i < argCount; ++i) {
|
||||
args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]);
|
||||
}
|
||||
var rv = rawInvoker.apply(null, args);
|
||||
rv = argTypes[0].fromWireType(rv);
|
||||
runDestructors(destructors);
|
||||
return rv;
|
||||
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
|
||||
};
|
||||
|
||||
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
|
||||
classType.registeredClass.instancePrototype[methodName] = function() {
|
||||
if (arguments.length !== argCount - 1) {
|
||||
throwBindingError('emscripten binding method ' + humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-1));
|
||||
}
|
||||
|
||||
var ptr = validateThis(this, classType, humanName);
|
||||
if (!isConst && this.$$.ptrType.isConst) {
|
||||
throwBindingError('Cannot call non-const method ' + humanName + ' on const reference');
|
||||
}
|
||||
|
||||
var destructors = [];
|
||||
var args = new Array(argCount + 1);
|
||||
args[0] = ptr;
|
||||
args[1] = memberFunction;
|
||||
for (var i = 1; i < argCount; ++i) {
|
||||
args[i + 1] = argTypes[i].toWireType(destructors, arguments[i - 1]);
|
||||
}
|
||||
var rv = rawInvoker.apply(null, args);
|
||||
rv = argTypes[0].fromWireType(rv);
|
||||
runDestructors(destructors);
|
||||
return rv;
|
||||
};
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
@ -999,13 +1025,21 @@ function __embind_register_class_class_function(
|
|||
rawInvoker,
|
||||
fn
|
||||
) {
|
||||
var classType = requireRegisteredType(rawClassType, 'class');
|
||||
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
|
||||
methodName = Pointer_stringify(methodName);
|
||||
rawInvoker = FUNCTION_TABLE[rawInvoker];
|
||||
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
|
||||
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
|
||||
classType = classType[0];
|
||||
var humanName = classType.name + '.' + methodName;
|
||||
classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
|
||||
|
||||
classType.registeredClass.constructor[methodName] = function() {
|
||||
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
|
||||
};
|
||||
|
||||
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
|
||||
classType.registeredClass.constructor[methodName] = makeInvoker(humanName, argCount, argTypes, rawInvoker, fn);
|
||||
return [];
|
||||
});
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
@ -1023,23 +1057,39 @@ function __embind_register_class_property(
|
|||
getter = FUNCTION_TABLE[getter];
|
||||
setter = FUNCTION_TABLE[setter];
|
||||
memberPointer = copyMemberPointer(memberPointer, memberPointerSize);
|
||||
whenDependentTypesAreResolved([], [rawClassType, rawFieldType], function(converters) {
|
||||
var classType = converters[0];
|
||||
var fieldType = converters[1];
|
||||
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
|
||||
classType = classType[0];
|
||||
var humanName = classType.name + '.' + fieldName;
|
||||
|
||||
Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
|
||||
get: function() {
|
||||
var ptr = validateThis(this, classType, humanName + ' getter');
|
||||
return fieldType.fromWireType(getter(ptr, memberPointer));
|
||||
throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
|
||||
},
|
||||
set: function(v) {
|
||||
var ptr = validateThis(this, classType, humanName + ' setter');
|
||||
var destructors = [];
|
||||
setter(ptr, memberPointer, fieldType.toWireType(destructors, v));
|
||||
runDestructors(destructors);
|
||||
set: function() {
|
||||
throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [rawFieldType]);
|
||||
},
|
||||
enumerable: true
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
whenDependentTypesAreResolved([], [rawFieldType], function(fieldType) {
|
||||
fieldType = fieldType[0];
|
||||
Object.defineProperty(classType.registeredClass.instancePrototype, fieldName, {
|
||||
get: function() {
|
||||
var ptr = validateThis(this, classType, humanName + ' getter');
|
||||
return fieldType.fromWireType(getter(ptr, memberPointer));
|
||||
},
|
||||
set: function(v) {
|
||||
var ptr = validateThis(this, classType, humanName + ' setter');
|
||||
var destructors = [];
|
||||
setter(ptr, memberPointer, fieldType.toWireType(destructors, v));
|
||||
runDestructors(destructors);
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
return [];
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,25 +8,24 @@
|
|||
|
||||
using namespace emscripten;
|
||||
|
||||
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;
|
||||
}
|
||||
extern "C" {
|
||||
const char* EMSCRIPTEN_KEEPALIVE __getTypeName(const std::type_info* ti) {
|
||||
int stat;
|
||||
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
|
||||
if (stat == 0 && demangled) {
|
||||
return demangled;
|
||||
}
|
||||
|
||||
switch (stat) {
|
||||
case -1:
|
||||
return "<allocation failure>";
|
||||
case -2:
|
||||
return "<invalid C++ symbol>";
|
||||
case -3:
|
||||
return "<invalid argument>";
|
||||
default:
|
||||
return "<unknown error>";
|
||||
switch (stat) {
|
||||
case -1:
|
||||
return strdup("<allocation failure>");
|
||||
case -2:
|
||||
return strdup("<invalid C++ symbol>");
|
||||
case -3:
|
||||
return strdup("<invalid argument>");
|
||||
default:
|
||||
return strdup("<unknown error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,6 +59,4 @@ EMSCRIPTEN_BINDINGS(native_and_builtin_types) {
|
|||
|
||||
_embind_register_cstring(TypeID<std::string>::get(), "std::string");
|
||||
_embind_register_emval(TypeID<val>::get(), "emscripten::val");
|
||||
|
||||
function("_embind_getTypeName", &_embind_getTypeName);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче