Skip to content

Commit

Permalink
JSG_STRUCT_TS_OVERRIDE_DYNAMIC by refactoring JSG_STRUCT (#2545)
Browse files Browse the repository at this point in the history
  • Loading branch information
AdityaAtulTewari authored Aug 21, 2024
1 parent 178c5f5 commit 03859f5
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 7 deletions.
49 changes: 43 additions & 6 deletions src/workerd/jsg/jsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,13 @@ using HasGetTemplateOverload =
static constexpr char _JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY[] = \
JSG_STRING_LITERAL(__VA_ARGS__)

// Like JSG_STRUCT_TS_OVERRIDE, however it enables dynamic selection of TS_OVERRIDE.
// Should be placed adjacent to the JSG_STRUCT delcaration, inside the same struct definition.
#define JSG_STRUCT_TS_OVERRIDE_DYNAMIC(...) \
static void jsgConfiguration(__VA_ARGS__); \
template <typename Registry> \
static void registerTypeScriptDynamicOverride(Registry& registry, ##__VA_ARGS__)

// Like JSG_TS_DEFINE but for use with JSG_STRUCT. Should be placed adjacent to the JSG_STRUCT
// declaration, inside the same `struct` definition. See the `## TypeScript`section of the JSG README.md
// for more details.
Expand Down Expand Up @@ -644,6 +651,20 @@ struct HasStructTypeScriptDefine<T, decltype(T::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE
// all in JavaScript when the optional is null in C++ (as opposed to the field being present but
// assigned the value `undefined`).
//
// Note that if a `validate` function is provided, then it will be called after the struct is
// unwrapped from v8. This would be an appropriate time to throw an error.
// Signature: void validate(jsg::Lock& js);
// Example:
// struct ValidatingFoo {
// kj::String abc;
// void validate(jsg::Lock& js) {
// JSG_REQUIRE(abc.size() != 0, TypeError, "Field 'abc' had no length in 'ValidatingFoo'.");
// }
// JSG_STRUCT(abc);
// };
//
// In this example the validate method would throw a `TypeError` if the size of the `abc` field was zero.
//
// Fields with a starting '$' will have that dollar sign prefix stripped in the JS binding. A
// motivating example to enact that change was WebCrypto which has a field in a dictionary called
// "public". '$' was chosen as a token we can use because it's a character for a C++ identifier. If
Expand All @@ -654,13 +675,20 @@ struct HasStructTypeScriptDefine<T, decltype(T::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE
#define JSG_STRUCT(...) \
static constexpr ::workerd::jsg::JsgKind JSG_KIND KJ_UNUSED = ::workerd::jsg::JsgKind::STRUCT; \
static constexpr char JSG_FOR_EACH(JSG_STRUCT_FIELD_NAME, , __VA_ARGS__); \
template <typename Registry, typename Self> \
static void registerMembers(Registry& registry) { \
template <typename TypeWrapper, typename Self> \
using JsgFieldWrappers = \
::workerd::jsg::TypeTuple<JSG_FOR_EACH(JSG_STRUCT_FIELD, , __VA_ARGS__)>; \
template <typename Registry, typename Self, typename Config> \
static void registerMembersInternal(Registry& registry, Config arg) { \
JSG_FOR_EACH(JSG_STRUCT_REGISTER_MEMBER, , __VA_ARGS__); \
if constexpr (::workerd::jsg::HasStructTypeScriptRoot<Self>::value) { \
registry.registerTypeScriptRoot(); \
} \
if constexpr (::workerd::jsg::HasStructTypeScriptOverride<Self>::value) { \
if constexpr (requires(jsg::GetConfiguration<Self> arg) { \
registerTypeScriptDynamicOverride<Registry>(registry, arg); \
}) { \
registerTypeScriptDynamicOverride<Registry>(registry, arg); \
} else if constexpr (::workerd::jsg::HasStructTypeScriptOverride<Self>::value) { \
registry.template registerTypeScriptOverride< \
Self::_JSG_STRUCT_TS_OVERRIDE_DO_NOT_USE_DIRECTLY>(); \
} \
Expand All @@ -669,9 +697,18 @@ struct HasStructTypeScriptDefine<T, decltype(T::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE
.template registerTypeScriptDefine<Self::_JSG_STRUCT_TS_DEFINE_DO_NOT_USE_DIRECTLY>(); \
} \
} \
template <typename TypeWrapper, typename Self> \
using JsgFieldWrappers = \
::workerd::jsg::TypeTuple<JSG_FOR_EACH(JSG_STRUCT_FIELD, , __VA_ARGS__)>
template <typename Registry, typename Self> \
static void registerMembers(Registry& registry) \
requires(!jsg::HasConfiguration<Self>) \
{ \
registerMembersInternal<Registry, Self, void*>(registry, nullptr); \
} \
template <typename Registry, typename Self> \
static void registerMembers(Registry& registry, jsg::GetConfiguration<Self> arg) \
requires jsg::HasConfiguration<Self> \
{ \
registerMembersInternal<Registry, Self, jsg::GetConfiguration<Self>>(registry, arg); \
}

namespace {
template <size_t N>
Expand Down
11 changes: 10 additions & 1 deletion src/workerd/jsg/struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,17 @@ class StructWrapper<Self, T, TypeTuple<FieldWrappers...>, kj::_::Indexes<indices
// it prescribes lexicographically-ordered member initialization, with base members ordered
// before derived members. Objects with mutating getters might be broken by this, but it
// doesn't seem worth fixing absent a compelling use case.
auto t = T{kj::get<indices>(fields).unwrap(static_cast<Self&>(*this), isolate, context, in)...};

// Note that if a `validate` function is provided, then it will be called after the struct is
// unwrapped from v8. This would be an appropriate time to throw an error.
// Signature: void validate(jsg::Lock& js);
if constexpr (requires(jsg::Lock& js) { t.validate(js); }) {
jsg::Lock& js = jsg::Lock::from(isolate);
t.validate(js);
}

return T{kj::get<indices>(fields).unwrap(static_cast<Self&>(*this), isolate, context, in)...};
return t;
}

void newContext() = delete;
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/jsg/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ auto getParameterType(void (*)(Arg)) -> Arg;
template <typename T>
using GetConfiguration = decltype(getParameterType(&T::jsgConfiguration));

template <typename T>
concept HasConfiguration = requires(GetConfiguration<T> arg) { T::jsgConfiguration(arg); };

inline bool isFinite(double value) {
return !(kj::isNaN(value) || value == kj::inf() || value == -kj::inf());
}
Expand Down

0 comments on commit 03859f5

Please sign in to comment.