// Copyright (c) 2017-2022 Cloudflare, Inc. // Licensed under the Apache 2.0 license found in the LICENSE file or at: // https://opensource.org/licenses/Apache-2.0 #pragma once // INTERNAL IMPLEMENTATION FILE // // Type traits and concepts to help us map between C++ and Web IDL types. #include #include #include #include #include namespace workerd::jsg::webidl { // ======================================================================================= // Base detection concepts and helpers // True if T has a JSG_KIND static member (i.e., is a JSG type). template concept HasJsgKind = requires { T::JSG_KIND; }; // Helper to detect and unwrap Ref types template struct RefTraits_ { static constexpr bool isRef = false; }; template struct RefTraits_> { static constexpr bool isRef = true; using Type = T; }; template concept IsRef = RefTraits_::isRef; template using RefType = RefTraits_::Type; // ======================================================================================= // Optional type detection template constexpr bool isOptional = false; template constexpr bool isOptional> = true; template constexpr bool isOptional> = true; template concept OptionalType = isOptional; // Counts the number of Web IDL nullable types (modeled with kj::Maybe in JSG) that exist in // `T...`. This variable template is designed to accept unflattened OneOfs -- it will recurse // manually through the OneOfs, meaning `nullableTypeCount>>> == 2`. // // Implements the "number of nullable member types" algorithm defined here: // https://heycam.github.io/webidl/#dfn-number-of-nullable-member-types template constexpr size_t nullableTypeCount = 0; template constexpr size_t nullableTypeCount = nullableTypeCount; template constexpr size_t nullableTypeCount, U...> = 1 + nullableTypeCount + nullableTypeCount; template constexpr size_t nullableTypeCount, U...> = nullableTypeCount + nullableTypeCount; // TODO(soon): What to do with Optional? Unwrap? Hard error? It's not nullable. // ======================================================================================= // Distinguishable type categories // // Web IDL defines nine different categories of distinguishable types, which are used to validate // union types. For a basic example, consider `kj::OneOf`. From Web IDL's perspective, // these are both numeric types, thus the union is invalid. // // Note that these categories do not cover all Web IDL types, like Promises. Such types are not // allowed in unions under any circumstances. // True if T is a Web IDL dictionary type (modeled with JSG_STRUCT). template concept DictionaryType = HasJsgKind && (T::JSG_KIND == JsgKind::STRUCT); // Note: This covers Web IDL exception types as well. This doesn't seem to be a problem in practice, // but it's worth knowing that the Web IDL spec considers the two categories distinct. template concept NonCallbackInterfaceType_ = HasJsgKind && (T::JSG_KIND == JsgKind::RESOURCE); // Helper to check if Ref wraps a resource type template constexpr bool isRefToResource_() { if constexpr (IsRef) { return NonCallbackInterfaceType_>; } else { return false; } } // True if T is a Web IDL non-callback interface type (modeled with JSG_RESOURCE). // Handles both T and Ref cases. template concept NonCallbackInterfaceType = NonCallbackInterfaceType_ || isRefToResource_(); template concept BufferSourceType = kj::isSameType>() || kj::isSameType>() || kj::isSameType>() || kj::isSameType>() || kj::isSameType(); // Helper for record type detection template struct IsRecordType_: std::false_type {}; template struct IsRecordType_>: std::true_type {}; template concept RecordType = IsRecordType_::value; template concept BooleanType = StrictlyBool || kj::isSameType>(); template concept IntegerType = kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType>(); template concept NumericType = IntegerType || kj::isSameType() || kj::isSameType>(); template concept StringType = kj::isSameType() || kj::isSameType() || kj::isSameType() || kj::isSameType>() || kj::isSameType>() || kj::isSameType>() || kj::isSameType>() || kj::isSameType>() || kj::isSameType(); template concept ObjectType = kj::isSameType>() || kj::isSameType>(); template concept SymbolType = false; // TODO(soon): kj::isSameType>()? // Helper for callback function type detection template struct IsCallbackFunctionType_: std::false_type {}; template struct IsCallbackFunctionType_>: std::true_type {}; template struct IsCallbackFunctionType_>: std::true_type {}; template concept CallbackFunctionType = IsCallbackFunctionType_::value; // True if T is a Web IDL buffer source type, exception type, or non-callback interface type. The // latter two cases are both modeled with JSG_RESOURCE_TYPE, which is why this trait only has two // predicates, rather than three. template concept InterfaceLikeType = BufferSourceType || NonCallbackInterfaceType; // TODO(someday): Or callback interface types. Callback interface types seem to be going the way of // the dodo -- fingers crossed that we won't have to implement them. template concept DictionaryLikeType = DictionaryType || RecordType; // Helper for sequence-like type detection template struct IsSequenceLikeType_: std::false_type {}; template struct IsSequenceLikeType_> : std::bool_constant() && !kj::isSameType()> {}; template struct IsSequenceLikeType_>: std::true_type {}; // TODO(soon): And frozen array types. template concept SequenceLikeType = IsSequenceLikeType_::value; // True if T is listed in the table in Web IDL's distinguishable type algorithm: // https://heycam.github.io/webidl/#dfn-distinguishable, step 4. template concept DistinguishableType = BooleanType || NumericType || StringType || ObjectType || SymbolType || InterfaceLikeType || CallbackFunctionType || DictionaryLikeType || SequenceLikeType; template concept IndistinguishableType = !DistinguishableType; // ======================================================================================= // Backward-compatible variable templates // // These provide backward compatibility with code that uses the old constexpr bool style // that cannot use the concepts directly. template constexpr bool isNonCallbackInterfaceType = NonCallbackInterfaceType; template constexpr bool isRecordType = RecordType; template constexpr bool isBooleanType = BooleanType; template constexpr bool isNumericType = NumericType; template constexpr bool isStringType = StringType; // ======================================================================================= // Type list utilities template constexpr bool hasDuplicateTypes = false; template constexpr bool hasDuplicateTypes = kj::isSameType() || hasDuplicateTypes || hasDuplicateTypes; // Traits computed over a flattened type list. Used for Web IDL union validation. // Uses the concept-based variable templates for counting. template struct FlattenedTypeTraits_ { static constexpr size_t dictionaryTypeCount = (static_cast(DictionaryType) + ...); static constexpr size_t booleanTypeCount = (static_cast(BooleanType) + ...); static constexpr size_t numericTypeCount = (static_cast(NumericType) + ...); static constexpr size_t stringTypeCount = (static_cast(StringType) + ...); static constexpr size_t objectTypeCount = (static_cast(ObjectType) + ...); static constexpr size_t symbolTypeCount = (static_cast(SymbolType) + ...); static constexpr size_t interfaceLikeTypeCount = (static_cast(InterfaceLikeType) + ...); static constexpr size_t callbackFunctionTypeCount = (static_cast(CallbackFunctionType) + ...); static constexpr size_t dictionaryLikeTypeCount = (static_cast(DictionaryLikeType) + ...); static constexpr size_t sequenceLikeTypeCount = (static_cast(SequenceLikeType) + ...); static constexpr bool hasDuplicateTypes = webidl::hasDuplicateTypes; static constexpr bool hasIndistinguishableTypes = (IndistinguishableType || ...); static constexpr bool hasOptionalTypes = (OptionalType || ...); }; template struct Flatten; template struct Flatten: Traits {}; template