Skip to content

Commit 2bbdb0c

Browse files
Chad Austinchadaustin
authored andcommitted
embind doesn't always need the full std::type_info record. if EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0, then use a lighter type identifier. This shaves 175 KB off of our engine's minified JavaScript.
1 parent ce58885 commit 2bbdb0c

5 files changed

Lines changed: 83 additions & 27 deletions

File tree

system/include/emscripten/bind.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,7 @@ namespace emscripten {
813813
// NOTE: this returns the class type, not the pointer type
814814
template<typename T>
815815
inline TYPEID getActualType(T* ptr) {
816-
return reinterpret_cast<TYPEID>(&typeid(*ptr));
816+
return getLightTypeID(*ptr);
817817
};
818818
}
819819

system/include/emscripten/wire.h

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,66 @@
1515
#define EMSCRIPTEN_ALWAYS_INLINE __attribute__((always_inline))
1616

1717
namespace emscripten {
18+
#ifndef EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES
19+
#define EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES 1
20+
#endif
21+
22+
23+
#if EMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES
24+
constexpr bool has_unbound_type_names = true;
25+
#else
26+
constexpr bool has_unbound_type_names = false;
27+
#endif
28+
1829
namespace internal {
1930
typedef void (*GenericFunction)();
2031

21-
typedef const struct _TYPEID* TYPEID;
32+
typedef const struct _TYPEID {}* TYPEID;
33+
34+
35+
// We don't need the full std::type_info implementation. We
36+
// just need a unique identifier per type and polymorphic type
37+
// identification.
38+
39+
template<typename T>
40+
struct CanonicalizedID {
41+
static TYPEID get() {
42+
static _TYPEID c;
43+
return &c;
44+
}
45+
};
46+
47+
template<typename T>
48+
struct Canonicalized {
49+
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type;
50+
};
51+
52+
template<typename T>
53+
struct LightTypeID {
54+
static TYPEID get() {
55+
typedef typename Canonicalized<T>::type C;
56+
if (has_unbound_type_names || std::is_polymorphic<C>::value) {
57+
return reinterpret_cast<TYPEID>(&typeid(C));
58+
} else {
59+
return CanonicalizedID<C>::get();
60+
}
61+
}
62+
};
63+
64+
template<typename T>
65+
const TYPEID getLightTypeID(const T& value) {
66+
typedef typename Canonicalized<T>::type C;
67+
if (has_unbound_type_names || std::is_polymorphic<C>::value) {
68+
return reinterpret_cast<TYPEID>(&typeid(value));
69+
} else {
70+
return LightTypeID<T>::get();
71+
}
72+
}
2273

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

@@ -53,7 +97,7 @@ namespace emscripten {
5397
template<typename T>
5498
struct TypeID<AllowedRawPointer<T>> {
5599
static TYPEID get() {
56-
return reinterpret_cast<TYPEID>(&typeid(T*));
100+
return LightTypeID<T*>::get();
57101
}
58102
};
59103

system/lib/embind/bind.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,32 @@ using namespace emscripten;
1414

1515
extern "C" {
1616
const char* __attribute__((used)) __getTypeName(const std::type_info* ti) {
17+
if (has_unbound_type_names) {
1718
#ifdef USE_CXA_DEMANGLE
18-
int stat;
19-
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
20-
if (stat == 0 && demangled) {
21-
return demangled;
22-
}
19+
int stat;
20+
char* demangled = abi::__cxa_demangle(ti->name(), NULL, NULL, &stat);
21+
if (stat == 0 && demangled) {
22+
return demangled;
23+
}
2324

24-
switch (stat) {
25-
case -1:
26-
return strdup("<allocation failure>");
27-
case -2:
28-
return strdup("<invalid C++ symbol>");
29-
case -3:
30-
return strdup("<invalid argument>");
31-
default:
32-
return strdup("<unknown error>");
33-
}
25+
switch (stat) {
26+
case -1:
27+
return strdup("<allocation failure>");
28+
case -2:
29+
return strdup("<invalid C++ symbol>");
30+
case -3:
31+
return strdup("<invalid argument>");
32+
default:
33+
return strdup("<unknown error>");
34+
}
3435
#else
35-
return strdup(ti->name());
36+
return strdup(ti->name());
3637
#endif
38+
} else {
39+
char str[80];
40+
sprintf(str, "%p", ti);
41+
return strdup(str);
42+
}
3743
}
3844
}
3945

tests/embind/embind.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,6 +1646,10 @@ module({
16461646
if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well!
16471647

16481648
BaseFixture.extend("unbound types", function() {
1649+
if (!cm.hasUnboundTypeNames) {
1650+
return;
1651+
}
1652+
16491653
function assertMessage(fn, message) {
16501654
var e = assert.throws(cm.UnboundTypeError, fn);
16511655
assert.equal(message, e.message);

tests/embind/embind_test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,8 @@ struct BoundClass {
21472147
};
21482148

21492149
EMSCRIPTEN_BINDINGS(incomplete) {
2150+
constant("hasUnboundTypeNames", emscripten::has_unbound_type_names);
2151+
21502152
function("getUnboundClass", &passThrough<UnboundClass>);
21512153

21522154
class_<HasUnboundBase, base<UnboundClass>>("HasUnboundBase")

0 commit comments

Comments
 (0)