cannam@62: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@62: // Licensed under the MIT License: cannam@62: // cannam@62: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@62: // of this software and associated documentation files (the "Software"), to deal cannam@62: // in the Software without restriction, including without limitation the rights cannam@62: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@62: // copies of the Software, and to permit persons to whom the Software is cannam@62: // furnished to do so, subject to the following conditions: cannam@62: // cannam@62: // The above copyright notice and this permission notice shall be included in cannam@62: // all copies or substantial portions of the Software. cannam@62: // cannam@62: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@62: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@62: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@62: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@62: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@62: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@62: // THE SOFTWARE. cannam@62: cannam@62: #ifndef CAPNP_RAW_SCHEMA_H_ cannam@62: #define CAPNP_RAW_SCHEMA_H_ cannam@62: cannam@62: #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) cannam@62: #pragma GCC system_header cannam@62: #endif cannam@62: cannam@62: #include "common.h" // for uint and friends cannam@62: cannam@62: #if _MSC_VER cannam@62: #include cannam@62: #endif cannam@62: cannam@62: namespace capnp { cannam@62: namespace _ { // private cannam@62: cannam@62: struct RawSchema; cannam@62: cannam@62: struct RawBrandedSchema { cannam@62: // Represents a combination of a schema and bindings for its generic parameters. cannam@62: // cannam@62: // Note that while we generate one `RawSchema` per type, we generate a `RawBrandedSchema` for cannam@62: // every _instance_ of a generic type -- or, at least, every instance that is actually used. For cannam@62: // generated-code types, we use template magic to initialize these. cannam@62: cannam@62: const RawSchema* generic; cannam@62: // Generic type which we're branding. cannam@62: cannam@62: struct Binding { cannam@62: uint8_t which; // Numeric value of one of schema::Type::Which. cannam@62: cannam@62: bool isImplicitParameter; cannam@62: // For AnyPointer, true if it's an implicit method parameter. cannam@62: cannam@62: uint16_t listDepth; // Number of times to wrap the base type in List(). cannam@62: cannam@62: uint16_t paramIndex; cannam@62: // For AnyPointer. If it's a type parameter (scopeId is non-zero) or it's an implicit parameter cannam@62: // (isImplicitParameter is true), then this is the parameter index. Otherwise this is a numeric cannam@62: // value of one of schema::Type::AnyPointer::Unconstrained::Which. cannam@62: cannam@62: union { cannam@62: const RawBrandedSchema* schema; // for struct, enum, interface cannam@62: uint64_t scopeId; // for AnyPointer, if it's a type parameter cannam@62: }; cannam@62: cannam@62: Binding() = default; cannam@62: inline constexpr Binding(uint8_t which, uint16_t listDepth, const RawBrandedSchema* schema) cannam@62: : which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(0), cannam@62: schema(schema) {} cannam@62: inline constexpr Binding(uint8_t which, uint16_t listDepth, cannam@62: uint64_t scopeId, uint16_t paramIndex) cannam@62: : which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(paramIndex), cannam@62: scopeId(scopeId) {} cannam@62: inline constexpr Binding(uint8_t which, uint16_t listDepth, uint16_t implicitParamIndex) cannam@62: : which(which), isImplicitParameter(true), listDepth(listDepth), cannam@62: paramIndex(implicitParamIndex), scopeId(0) {} cannam@62: }; cannam@62: cannam@62: struct Scope { cannam@62: uint64_t typeId; cannam@62: // Type ID whose parameters are being bound. cannam@62: cannam@62: const Binding* bindings; cannam@62: uint bindingCount; cannam@62: // Bindings for those parameters. cannam@62: cannam@62: bool isUnbound; cannam@62: // This scope is unbound, in the sense of SchemaLoader::getUnbound(). cannam@62: }; cannam@62: cannam@62: const Scope* scopes; cannam@62: // Array of enclosing scopes for which generic variables have been bound, sorted by type ID. cannam@62: cannam@62: struct Dependency { cannam@62: uint location; cannam@62: const RawBrandedSchema* schema; cannam@62: }; cannam@62: cannam@62: const Dependency* dependencies; cannam@62: // Map of branded schemas for dependencies of this type, given our brand. Only dependencies that cannam@62: // are branded are included in this map; if a dependency is missing, use its `defaultBrand`. cannam@62: cannam@62: uint32_t scopeCount; cannam@62: uint32_t dependencyCount; cannam@62: cannam@62: enum class DepKind { cannam@62: // Component of a Dependency::location. Specifies what sort of dependency this is. cannam@62: cannam@62: INVALID, cannam@62: // Mostly defined to ensure that zero is not a valid location. cannam@62: cannam@62: FIELD, cannam@62: // Binding needed for a field's type. The index is the field index (NOT ordinal!). cannam@62: cannam@62: METHOD_PARAMS, cannam@62: // Bindings needed for a method's params type. The index is the method number. cannam@62: cannam@62: METHOD_RESULTS, cannam@62: // Bindings needed for a method's results type. The index is the method ordinal. cannam@62: cannam@62: SUPERCLASS, cannam@62: // Bindings needed for a superclass type. The index is the superclass's index in the cannam@62: // "extends" list. cannam@62: cannam@62: CONST_TYPE cannam@62: // Bindings needed for the type of a constant. The index is zero. cannam@62: }; cannam@62: cannam@62: static inline uint makeDepLocation(DepKind kind, uint index) { cannam@62: // Make a number representing the location of a particular dependency within its parent cannam@62: // schema. cannam@62: cannam@62: return (static_cast(kind) << 24) | index; cannam@62: } cannam@62: cannam@62: class Initializer { cannam@62: public: cannam@62: virtual void init(const RawBrandedSchema* generic) const = 0; cannam@62: }; cannam@62: cannam@62: const Initializer* lazyInitializer; cannam@62: // Lazy initializer, invoked by ensureInitialized(). cannam@62: cannam@62: inline void ensureInitialized() const { cannam@62: // Lazy initialization support. Invoke to ensure that initialization has taken place. This cannam@62: // is required in particular when traversing the dependency list. RawSchemas for compiled-in cannam@62: // types are always initialized; only dynamically-loaded schemas may be lazy. cannam@62: cannam@62: #if __GNUC__ cannam@62: const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE); cannam@62: #elif _MSC_VER cannam@62: const Initializer* i = *static_cast(&lazyInitializer); cannam@62: std::atomic_thread_fence(std::memory_order_acquire); cannam@62: #else cannam@62: #error "Platform not supported" cannam@62: #endif cannam@62: if (i != nullptr) i->init(this); cannam@62: } cannam@62: cannam@62: inline bool isUnbound() const; cannam@62: // Checks if this schema is the result of calling SchemaLoader::getUnbound(), in which case cannam@62: // binding lookups need to be handled specially. cannam@62: }; cannam@62: cannam@62: struct RawSchema { cannam@62: // The generated code defines a constant RawSchema for every compiled declaration. cannam@62: // cannam@62: // This is an internal structure which could change in the future. cannam@62: cannam@62: uint64_t id; cannam@62: cannam@62: const word* encodedNode; cannam@62: // Encoded SchemaNode, readable via readMessageUnchecked(encodedNode). cannam@62: cannam@62: uint32_t encodedSize; cannam@62: // Size of encodedNode, in words. cannam@62: cannam@62: const RawSchema* const* dependencies; cannam@62: // Pointers to other types on which this one depends, sorted by ID. The schemas in this table cannam@62: // may be uninitialized -- you must call ensureInitialized() on the one you wish to use before cannam@62: // using it. cannam@62: // cannam@62: // TODO(someday): Make this a hashtable. cannam@62: cannam@62: const uint16_t* membersByName; cannam@62: // Indexes of members sorted by name. Used to implement name lookup. cannam@62: // TODO(someday): Make this a hashtable. cannam@62: cannam@62: uint32_t dependencyCount; cannam@62: uint32_t memberCount; cannam@62: // Sizes of above tables. cannam@62: cannam@62: const uint16_t* membersByDiscriminant; cannam@62: // List of all member indexes ordered by discriminant value. Those which don't have a cannam@62: // discriminant value are listed at the end, in order by ordinal. cannam@62: cannam@62: const RawSchema* canCastTo; cannam@62: // Points to the RawSchema of a compiled-in type to which it is safe to cast any DynamicValue cannam@62: // with this schema. This is null for all compiled-in types; it is only set by SchemaLoader on cannam@62: // dynamically-loaded types. cannam@62: cannam@62: class Initializer { cannam@62: public: cannam@62: virtual void init(const RawSchema* schema) const = 0; cannam@62: }; cannam@62: cannam@62: const Initializer* lazyInitializer; cannam@62: // Lazy initializer, invoked by ensureInitialized(). cannam@62: cannam@62: inline void ensureInitialized() const { cannam@62: // Lazy initialization support. Invoke to ensure that initialization has taken place. This cannam@62: // is required in particular when traversing the dependency list. RawSchemas for compiled-in cannam@62: // types are always initialized; only dynamically-loaded schemas may be lazy. cannam@62: cannam@62: #if __GNUC__ cannam@62: const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE); cannam@62: #elif _MSC_VER cannam@62: const Initializer* i = *static_cast(&lazyInitializer); cannam@62: std::atomic_thread_fence(std::memory_order_acquire); cannam@62: #else cannam@62: #error "Platform not supported" cannam@62: #endif cannam@62: if (i != nullptr) i->init(this); cannam@62: } cannam@62: cannam@62: RawBrandedSchema defaultBrand; cannam@62: // Specifies the brand to use for this schema if no generic parameters have been bound to cannam@62: // anything. Generally, in the default brand, all generic parameters are treated as if they were cannam@62: // bound to `AnyPointer`. cannam@62: }; cannam@62: cannam@62: inline bool RawBrandedSchema::isUnbound() const { cannam@62: // The unbound schema is the only one that has no scopes but is not the default schema. cannam@62: return scopeCount == 0 && this != &generic->defaultBrand; cannam@62: } cannam@62: cannam@62: } // namespace _ (private) cannam@62: } // namespace capnp cannam@62: cannam@62: #endif // CAPNP_RAW_SCHEMA_H_