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