#include #include <../lib/libcxxabi/src/private_typeinfo.h> #include <../lib/libcxxabi/include/cxxabi.h> #include #include #include #include #include using namespace emscripten; namespace __cxxabiv1 { std::vector __getBaseClasses(const __class_type_info* cti) { std::vector bases; const __si_class_type_info* scti = dynamic_cast(cti); if (scti) { bases.emplace_back(scti->__base_type); } else { const __vmi_class_type_info* vcti = dynamic_cast(cti); if (vcti) { for (int i = 0; i < vcti->__base_count; i++) { bases.emplace_back(vcti->__base_info[i].__base_type); } } } return bases; } int __getBaseOffset(const __class_type_info* ctiDv, const __class_type_info* ctiBs) { int offset = 0; const __vmi_class_type_info* vcti = dynamic_cast(ctiDv); if (vcti) { for (int i = 0; i < vcti->__base_count; i++) { if (vcti->__base_info[i].__base_type == ctiBs) { offset = vcti->__base_info[i].__offset_flags >> __base_class_type_info::__offset_shift; break; } } } return offset; } void __getDerivationPaths(const __class_type_info* dv, const __class_type_info* bs, std::vectorpath, std::vector>& paths) { std::vector newPath(path); newPath.emplace_back(dv); if (dv == bs) { paths.emplace_back(newPath); } else { std::vector bases = __getBaseClasses(dv); for (int i = 0; i < bases.size(); i++) { __getDerivationPaths(bases[i], bs, newPath, paths); } } } int __pathOffset(std::vector path) { int offset = 0; for (int i = 0; i < path.size()-1; i++) { offset += __getBaseOffset(path[i], path[i+1]); } return offset; } } namespace emscripten { namespace internal { void registerStandardTypes() { static bool first = true; if (first) { first = false; _embind_register_void(TypeID::get(), "void"); _embind_register_bool(TypeID::get(), "bool", true, false); _embind_register_integer(TypeID::get(), "char"); _embind_register_integer(TypeID::get(), "signed char"); _embind_register_integer(TypeID::get(), "unsigned char"); _embind_register_integer(TypeID::get(), "short"); _embind_register_integer(TypeID::get(), "unsigned short"); _embind_register_integer(TypeID::get(), "int"); _embind_register_integer(TypeID::get(), "unsigned int"); _embind_register_integer(TypeID::get(), "long"); _embind_register_integer(TypeID::get(), "unsigned long"); _embind_register_float(TypeID::get(), "float"); _embind_register_float(TypeID::get(), "double"); _embind_register_cstring(TypeID::get(), "std::string"); _embind_register_emval(TypeID::get(), "emscripten::val"); } } extern "C" { // These three routines constitute an extension to the compiler's support for dynamic type conversion. // They are used by embind.js to implement automatic downcasting of return values which are pointers to // polymorphic objects. // __getDerivationPath returns an array of type_info pointers describing the derivation chain starting with // the derived type and proceeding toward (and ending with) the base type. Types are only included if they // appear on all possible derivation paths. std::vector __getDerivationPath(int dv, const int bs) { std::vector> paths; const std::type_info* dv1 = (std::type_info*)dv; const std::type_info* bs1 = (std::type_info*)bs; const __cxxabiv1::__class_type_info* dv2 = dynamic_cast(dv1); const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); if (dv2 && bs2) { __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); } std::vector derivationPath; if (paths.size() > 0) { for (int j = 0; j < paths[0].size(); j++) { bool disqualified = false; for (int i = 1; i < paths.size(); i++) { bool found = false; for (int j2 = 0; j2 < paths[i].size(); j2++) { if (paths[i][j2] == paths[0][j]) { found = true; break; } } if (!found) { disqualified = true; break; } } if (!disqualified) { derivationPath.emplace_back((int)paths[0][j]); } } } return derivationPath; } void* EMSCRIPTEN_KEEPALIVE __staticPointerCast(void* p, int dv, int bs) { std::vector> paths; int direction = 1; const std::type_info* dv1 = (std::type_info*)dv; const std::type_info* bs1 = (std::type_info*)bs; const __cxxabiv1::__class_type_info* dv2 = dynamic_cast(dv1); const __cxxabiv1::__class_type_info* bs2 = dynamic_cast(bs1); if (dv2 && bs2) { __cxxabiv1::__getDerivationPaths(dv2, bs2, std::vector(), paths); if (paths.size() == 0) { __cxxabiv1::__getDerivationPaths(bs2, dv2, std::vector(), paths); direction = -1; } } int offset = -1; for (int i = 0; i < paths.size(); i++) { if (offset < 0) { offset = __cxxabiv1::__pathOffset(paths[i]); } else { if (offset != __cxxabiv1::__pathOffset(paths[i])) { return (void *)-2; // ambiguous cast -- throw instead? } } } if (offset < 0) { return (void *)-1; // types are not related -- throw instead? } if (p == 0) { return (void *)0; } return (void *)((int)p + offset * direction); } // __getDynamicPointerType returns (for polymorphic types only!) the type of the instance actually // pointed to. int EMSCRIPTEN_KEEPALIVE __getDynamicPointerType(int p) { void** vtable = *(void***)p; return (int)static_cast(vtable[-1]); } // Calls to __dynamic_cast are generated by the compiler to implement dynamic_cast<>() -- its prototype is // not available through any header file. It is called directly here because it allows run-time // specification of the target pointer type (which can only be specified at compile time when using // dynamic_cast<>(). void* __dynamic_cast(void*, const std::type_info*, const std::type_info*, int); // __dynamicPointerCast performs a C++ dynamic_cast<>() operation, but allowing run-time specification of // the from and to pointer types. int EMSCRIPTEN_KEEPALIVE __dynamicPointerCast(int p, int from, int to) { // The final parameter is a place-holder for a hint, a feature which is not currently implemented // in the emscripten runtime. The compiler passes a dummy value of -1, and so do we. return (int)__dynamic_cast((void *)p, (const std::type_info*)from, (const std::type_info *)to, -1); } const char* EMSCRIPTEN_KEEPALIVE __typeName(int p) { const std::type_info* ti = (const std::type_info*)p; size_t nameLen = std::min(strlen(ti->name()), (unsigned int)1024); char* name = (char *)malloc(nameLen+1); int stat; __cxxabiv1::__cxa_demangle(ti->name(), name, &nameLen, &stat); if (stat != 0) { strncpy(name, ti->name(), nameLen); name[nameLen] = '\0'; } return name; } EMSCRIPTEN_BINDINGS(([]() { // We bind __getDerivationPath in order to take advantage of the std::vector to Javascript array // conversion for the return value. This has the unfortunate side-effect of exposing it to third party // developers, but perhaps the double underscore will scare them away from calling it. function("__getDerivationPath", &__getDerivationPath); })); } } }