1// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22#pragma once
23
24#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
25#pragma GCC system_header
26#endif
27
28#include "common.h" // for uint and friends
29
30#if _MSC_VER
31#include <atomic>
32#endif
33
34namespace capnp {
35namespace _ { // private
36
37struct RawSchema;
38
39struct RawBrandedSchema {
40 // Represents a combination of a schema and bindings for its generic parameters.
41 //
42 // Note that while we generate one `RawSchema` per type, we generate a `RawBrandedSchema` for
43 // every _instance_ of a generic type -- or, at least, every instance that is actually used. For
44 // generated-code types, we use template magic to initialize these.
45
46 const RawSchema* generic;
47 // Generic type which we're branding.
48
49 struct Binding {
50 uint8_t which; // Numeric value of one of schema::Type::Which.
51
52 bool isImplicitParameter;
53 // For AnyPointer, true if it's an implicit method parameter.
54
55 uint16_t listDepth; // Number of times to wrap the base type in List().
56
57 uint16_t paramIndex;
58 // For AnyPointer. If it's a type parameter (scopeId is non-zero) or it's an implicit parameter
59 // (isImplicitParameter is true), then this is the parameter index. Otherwise this is a numeric
60 // value of one of schema::Type::AnyPointer::Unconstrained::Which.
61
62 union {
63 const RawBrandedSchema* schema; // for struct, enum, interface
64 uint64_t scopeId; // for AnyPointer, if it's a type parameter
65 };
66
67 Binding() = default;
68 inline constexpr Binding(uint8_t which, uint16_t listDepth, const RawBrandedSchema* schema)
69 : which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(0),
70 schema(schema) {}
71 inline constexpr Binding(uint8_t which, uint16_t listDepth,
72 uint64_t scopeId, uint16_t paramIndex)
73 : which(which), isImplicitParameter(false), listDepth(listDepth), paramIndex(paramIndex),
74 scopeId(scopeId) {}
75 inline constexpr Binding(uint8_t which, uint16_t listDepth, uint16_t implicitParamIndex)
76 : which(which), isImplicitParameter(true), listDepth(listDepth),
77 paramIndex(implicitParamIndex), scopeId(0) {}
78 };
79
80 struct Scope {
81 uint64_t typeId;
82 // Type ID whose parameters are being bound.
83
84 const Binding* bindings;
85 uint bindingCount;
86 // Bindings for those parameters.
87
88 bool isUnbound;
89 // This scope is unbound, in the sense of SchemaLoader::getUnbound().
90 };
91
92 const Scope* scopes;
93 // Array of enclosing scopes for which generic variables have been bound, sorted by type ID.
94
95 struct Dependency {
96 uint location;
97 const RawBrandedSchema* schema;
98 };
99
100 const Dependency* dependencies;
101 // Map of branded schemas for dependencies of this type, given our brand. Only dependencies that
102 // are branded are included in this map; if a dependency is missing, use its `defaultBrand`.
103
104 uint32_t scopeCount;
105 uint32_t dependencyCount;
106
107 enum class DepKind {
108 // Component of a Dependency::location. Specifies what sort of dependency this is.
109
110 INVALID,
111 // Mostly defined to ensure that zero is not a valid location.
112
113 FIELD,
114 // Binding needed for a field's type. The index is the field index (NOT ordinal!).
115
116 METHOD_PARAMS,
117 // Bindings needed for a method's params type. The index is the method number.
118
119 METHOD_RESULTS,
120 // Bindings needed for a method's results type. The index is the method ordinal.
121
122 SUPERCLASS,
123 // Bindings needed for a superclass type. The index is the superclass's index in the
124 // "extends" list.
125
126 CONST_TYPE
127 // Bindings needed for the type of a constant. The index is zero.
128 };
129
130 static inline uint makeDepLocation(DepKind kind, uint index) {
131 // Make a number representing the location of a particular dependency within its parent
132 // schema.
133
134 return (static_cast<uint>(kind) << 24) | index;
135 }
136
137 class Initializer {
138 public:
139 virtual void init(const RawBrandedSchema* generic) const = 0;
140 };
141
142 const Initializer* lazyInitializer;
143 // Lazy initializer, invoked by ensureInitialized().
144
145 inline void ensureInitialized() const {
146 // Lazy initialization support. Invoke to ensure that initialization has taken place. This
147 // is required in particular when traversing the dependency list. RawSchemas for compiled-in
148 // types are always initialized; only dynamically-loaded schemas may be lazy.
149
150#if __GNUC__
151 const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE);
152#elif _MSC_VER
153 const Initializer* i = *static_cast<Initializer const* const volatile*>(&lazyInitializer);
154 std::atomic_thread_fence(std::memory_order_acquire);
155#else
156#error "Platform not supported"
157#endif
158 if (i != nullptr) i->init(this);
159 }
160
161 inline bool isUnbound() const;
162 // Checks if this schema is the result of calling SchemaLoader::getUnbound(), in which case
163 // binding lookups need to be handled specially.
164};
165
166struct RawSchema {
167 // The generated code defines a constant RawSchema for every compiled declaration.
168 //
169 // This is an internal structure which could change in the future.
170
171 uint64_t id;
172
173 const word* encodedNode;
174 // Encoded SchemaNode, readable via readMessageUnchecked<schema::Node>(encodedNode).
175
176 uint32_t encodedSize;
177 // Size of encodedNode, in words.
178
179 const RawSchema* const* dependencies;
180 // Pointers to other types on which this one depends, sorted by ID. The schemas in this table
181 // may be uninitialized -- you must call ensureInitialized() on the one you wish to use before
182 // using it.
183 //
184 // TODO(someday): Make this a hashtable.
185
186 const uint16_t* membersByName;
187 // Indexes of members sorted by name. Used to implement name lookup.
188 // TODO(someday): Make this a hashtable.
189
190 uint32_t dependencyCount;
191 uint32_t memberCount;
192 // Sizes of above tables.
193
194 const uint16_t* membersByDiscriminant;
195 // List of all member indexes ordered by discriminant value. Those which don't have a
196 // discriminant value are listed at the end, in order by ordinal.
197
198 const RawSchema* canCastTo;
199 // Points to the RawSchema of a compiled-in type to which it is safe to cast any DynamicValue
200 // with this schema. This is null for all compiled-in types; it is only set by SchemaLoader on
201 // dynamically-loaded types.
202
203 class Initializer {
204 public:
205 virtual void init(const RawSchema* schema) const = 0;
206 };
207
208 const Initializer* lazyInitializer;
209 // Lazy initializer, invoked by ensureInitialized().
210
211 inline void ensureInitialized() const {
212 // Lazy initialization support. Invoke to ensure that initialization has taken place. This
213 // is required in particular when traversing the dependency list. RawSchemas for compiled-in
214 // types are always initialized; only dynamically-loaded schemas may be lazy.
215
216#if __GNUC__
217 const Initializer* i = __atomic_load_n(&lazyInitializer, __ATOMIC_ACQUIRE);
218#elif _MSC_VER
219 const Initializer* i = *static_cast<Initializer const* const volatile*>(&lazyInitializer);
220 std::atomic_thread_fence(std::memory_order_acquire);
221#else
222#error "Platform not supported"
223#endif
224 if (i != nullptr) i->init(this);
225 }
226
227 RawBrandedSchema defaultBrand;
228 // Specifies the brand to use for this schema if no generic parameters have been bound to
229 // anything. Generally, in the default brand, all generic parameters are treated as if they were
230 // bound to `AnyPointer`.
231};
232
233inline bool RawBrandedSchema::isUnbound() const {
234 // The unbound schema is the only one that has no scopes but is not the default schema.
235 return scopeCount == 0 && this != &generic->defaultBrand;
236}
237
238} // namespace _ (private)
239} // namespace capnp
240