Skip to content

Commit d85a30d

Browse files
committed
Use type tagging for OpaqueWrappable type checks
Avoids using dynamic_cast/RTTI by using unique type tags for each type T.
1 parent e43a3f5 commit d85a30d

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

src/workerd/jsg/promise.h

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,23 @@ namespace workerd::jsg {
2525
template <typename T, bool = isGcVisitable<T>()>
2626
struct OpaqueWrappable;
2727

28+
// Unique type tag for each type T. Used to implement fast type checking in OpaqueWrappable
29+
// without relying on dynamic_cast/RTTI. Each instantiation of this template variable has a
30+
// unique address that serves as a type identifier.
31+
template <typename T>
32+
inline const char opaqueTypeTag = 0;
33+
34+
template <typename T>
35+
inline const void* getOpaqueTypeTag() {
36+
return &opaqueTypeTag<T>;
37+
}
38+
2839
struct OpaqueWrappableBase: public Wrappable {
40+
// Type tag for fast type checking. Set by OpaqueWrappable<T> constructor.
41+
const void* const typeTag;
42+
43+
explicit OpaqueWrappableBase(const void* tag): typeTag(tag) {}
44+
2945
kj::StringPtr jsgGetMemoryName() const override final {
3046
return "OpaqueWrappable"_kjc;
3147
}
@@ -38,7 +54,7 @@ template <typename T>
3854
struct OpaqueWrappable<T, false>: public OpaqueWrappableBase {
3955
// Used to implement wrapOpaque().
4056

41-
OpaqueWrappable(T&& value): value(kj::mv(value)) {}
57+
OpaqueWrappable(T&& value): OpaqueWrappableBase(getOpaqueTypeTag<T>()), value(kj::mv(value)) {}
4258

4359
T value;
4460
bool movedAway = false;
@@ -95,8 +111,9 @@ T unwrapOpaque(v8::Isolate* isolate, v8::Local<v8::Value> handle) {
95111
static_assert(!isV8Local<T>(), "can't opaque-wrap non-persistent handles");
96112

97113
Wrappable& wrappable = KJ_ASSERT_NONNULL(Wrappable::tryUnwrapOpaque(isolate, handle));
98-
OpaqueWrappable<T>* holder = dynamic_cast<OpaqueWrappable<T>*>(&wrappable);
99-
KJ_ASSERT(holder != nullptr);
114+
auto* base = static_cast<OpaqueWrappableBase*>(&wrappable);
115+
KJ_ASSERT(base->typeTag == getOpaqueTypeTag<T>());
116+
auto* holder = static_cast<OpaqueWrappable<T>*>(base);
100117
KJ_ASSERT(!holder->movedAway);
101118
holder->movedAway = true;
102119
return kj::mv(holder->value);
@@ -111,8 +128,9 @@ T& unwrapOpaqueRef(v8::Isolate* isolate, v8::Local<v8::Value> handle) {
111128
static_assert(!isV8Local<T>(), "can't opaque-wrap non-persistent handles");
112129

113130
Wrappable& wrappable = KJ_ASSERT_NONNULL(Wrappable::tryUnwrapOpaque(isolate, handle));
114-
OpaqueWrappable<T>* holder = dynamic_cast<OpaqueWrappable<T>*>(&wrappable);
115-
KJ_ASSERT(holder != nullptr);
131+
auto* base = static_cast<OpaqueWrappableBase*>(&wrappable);
132+
KJ_ASSERT(base->typeTag == getOpaqueTypeTag<T>());
133+
auto* holder = static_cast<OpaqueWrappable<T>*>(base);
116134
KJ_ASSERT(!holder->movedAway);
117135
return holder->value;
118136
}
@@ -126,8 +144,9 @@ void dropOpaque(v8::Isolate* isolate, v8::Local<v8::Value> handle) {
126144
static_assert(!isV8Ref<T>());
127145

128146
KJ_IF_SOME(wrappable, Wrappable::tryUnwrapOpaque(isolate, handle)) {
129-
OpaqueWrappable<T>* holder = dynamic_cast<OpaqueWrappable<T>*>(&wrappable);
130-
if (holder != nullptr) {
147+
auto* base = static_cast<OpaqueWrappableBase*>(&wrappable);
148+
if (base->typeTag == getOpaqueTypeTag<T>()) {
149+
auto* holder = static_cast<OpaqueWrappable<T>*>(base);
131150
holder->movedAway = true;
132151
auto drop KJ_UNUSED = kj::mv(holder->value);
133152
}

0 commit comments

Comments
 (0)