Skip to content

Commit

Permalink
embind doesn't always need the full std::type_info record. if EMSCRIP…
Browse files Browse the repository at this point in the history
…TEN_HAS_UNBOUND_TYPE_NAMES=0, then use a lighter type identifier. This shaves 175 KB off of our engine's minified JavaScript.
  • Loading branch information
Chad Austin authored and chadaustin committed Apr 13, 2014
1 parent ce58885 commit 2bbdb0c
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 27 deletions.
2 changes: 1 addition & 1 deletion system/include/emscripten/bind.h
Expand Up @@ -813,7 +813,7 @@ namespace emscripten {
// NOTE: this returns the class type, not the pointer type
template<typename T>
inline TYPEID getActualType(T* ptr) {
return reinterpret_cast<TYPEID>(&typeid(*ptr));
return getLightTypeID(*ptr);
};
}

Expand Down
64 changes: 54 additions & 10 deletions system/include/emscripten/wire.h
Expand Up @@ -15,22 +15,66 @@
#define EMSCRIPTEN_ALWAYS_INLINE __attribute__((always_inline))

namespace emscripten {
#ifndef EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES
#define EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES 1
#endif


#if EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES
constexpr bool has_unbound_type_names = true;
#else
constexpr bool has_unbound_type_names = false;
#endif

namespace internal {
typedef void (*GenericFunction)();

typedef const struct _TYPEID* TYPEID;
typedef const struct _TYPEID {}* TYPEID;


// We don't need the full std::type_info implementation. We
// just need a unique identifier per type and polymorphic type
// identification.

template<typename T>
struct CanonicalizedID {
static TYPEID get() {
static _TYPEID c;
return &c;
}
};

template<typename T>
struct Canonicalized {
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type;
};

template<typename T>
struct LightTypeID {
static TYPEID get() {
typedef typename Canonicalized<T>::type C;
if (has_unbound_type_names || std::is_polymorphic<C>::value) {
return reinterpret_cast<TYPEID>(&typeid(C));
} else {
return CanonicalizedID<C>::get();
}
}
};

template<typename T>
const TYPEID getLightTypeID(const T& value) {
typedef typename Canonicalized<T>::type C;
if (has_unbound_type_names || std::is_polymorphic<C>::value) {
return reinterpret_cast<TYPEID>(&typeid(value));
} else {
return LightTypeID<T>::get();
}
}

// This implementation is technically not legal, as it's not
// required that two calls to typeid produce the same exact
// std::type_info instance. That said, it's likely to work
// given Emscripten compiles everything into one binary.
// Should it not work in the future: replace TypeID with an
// int, and store all TypeInfo we see in a map, allocating new
// TypeIDs as we add new items to the map.
template<typename T>
struct TypeID {
static TYPEID get() {
return reinterpret_cast<TYPEID>(&typeid(T));
return LightTypeID<T>::get();
}
};

Expand All @@ -53,7 +97,7 @@ namespace emscripten {
template<typename T>
struct TypeID<AllowedRawPointer<T>> {
static TYPEID get() {
return reinterpret_cast<TYPEID>(&typeid(T*));
return LightTypeID<T*>::get();
}
};

Expand Down
38 changes: 22 additions & 16 deletions system/lib/embind/bind.cpp
Expand Up @@ -14,26 +14,32 @@ using namespace emscripten;

extern "C" {
const char* __attribute__((used)) __getTypeName(const std::type_info* ti) {
if (has_unbound_type_names) {
#ifdef USE_CXA_DEMANGLE
int stat;
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
if (stat == 0 && demangled) {
return demangled;
}
int stat;
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
if (stat == 0 && demangled) {
return demangled;
}

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>");
}
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>");
}
#else
return strdup(ti->name());
return strdup(ti->name());
#endif
} else {
char str[80];
sprintf(str, "%p", ti);
return strdup(str);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions tests/embind/embind.test.js
Expand Up @@ -1646,6 +1646,10 @@ module({
if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!

BaseFixture.extend("unbound types", function() {
if (!cm.hasUnboundTypeNames) {
return;
}

function assertMessage(fn, message) {
var e = assert.throws(cm.UnboundTypeError, fn);
assert.equal(message, e.message);
Expand Down
2 changes: 2 additions & 0 deletions tests/embind/embind_test.cpp
Expand Up @@ -2147,6 +2147,8 @@ struct BoundClass {
};

EMSCRIPTEN_BINDINGS(incomplete) {
constant("hasUnboundTypeNames", emscripten::has_unbound_type_names);

function("getUnboundClass", &passThrough<UnboundClass>);

class_<HasUnboundBase, base<UnboundClass>>("HasUnboundBase")
Expand Down

0 comments on commit 2bbdb0c

Please sign in to comment.