Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSG_STRUCT_TS_OVERRIDE_DYNAMIC by refactoring JSG_STRUCT #2545

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions src/workerd/jsg/jsg.h
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,13 @@ using HasGetTemplateOverload = decltype(
#define JSG_STRUCT_TS_OVERRIDE(...) \
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 @@ -641,6 +648,20 @@ namespace {
// 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 @@ -652,23 +673,37 @@ namespace {
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>(); \
} \
if constexpr (::workerd::jsg::HasStructTypeScriptDefine<Self>::value) { \
registry.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) \
AdityaAtulTewari marked this conversation as resolved.
Show resolved Hide resolved
requires jsg::HasConfiguration<Self> { \
registerMembersInternal<Registry, Self, jsg::GetConfiguration<Self>>(registry, arg); \
}

namespace {
template <size_t N>
Expand Down
13 changes: 11 additions & 2 deletions src/workerd/jsg/struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,19 @@ 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.

return T {
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;
}

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 @@ -411,6 +411,9 @@ template <typename Arg> auto getParameterType(void (*)(Arg)) -> Arg;
// SFINAE-friendly accessor for a resource type's configuration parameter.
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
Loading