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 | #include "node-translator.h" |
23 | #include "parser.h" // only for generateGroupId() |
24 | #include <capnp/serialize.h> |
25 | #include <kj/debug.h> |
26 | #include <kj/arena.h> |
27 | #include <kj/encoding.h> |
28 | #include <set> |
29 | #include <map> |
30 | #include <stdlib.h> |
31 | |
32 | namespace capnp { |
33 | namespace compiler { |
34 | |
35 | bool shouldDetectIssue344() { |
36 | return getenv("CAPNP_IGNORE_ISSUE_344" ) == nullptr; |
37 | } |
38 | |
39 | class NodeTranslator::StructLayout { |
40 | // Massive, disgusting class which implements the layout algorithm, which decides the offset |
41 | // for each field. |
42 | |
43 | public: |
44 | template <typename UIntType> |
45 | struct HoleSet { |
46 | inline HoleSet(): holes{0, 0, 0, 0, 0, 0} {} |
47 | |
48 | // Represents a set of "holes" within a segment of allocated space, up to one hole of each |
49 | // power-of-two size between 1 bit and 32 bits. |
50 | // |
51 | // The amount of "used" space in a struct's data segment can always be represented as a |
52 | // combination of a word count and a HoleSet. The HoleSet represents the space lost to |
53 | // "padding". |
54 | // |
55 | // There can never be more than one hole of any particular size. Why is this? Well, consider |
56 | // that every data field has a power-of-two size, every field must be aligned to a multiple of |
57 | // its size, and the maximum size of a single field is 64 bits. If we need to add a new field |
58 | // of N bits, there are two possibilities: |
59 | // 1. A hole of size N or larger exists. In this case, we find the smallest hole that is at |
60 | // least N bits. Let's say that that hole has size M. We allocate the first N bits of the |
61 | // hole to the new field. The remaining M - N bits become a series of holes of sizes N*2, |
62 | // N*4, ..., M / 2. We know no holes of these sizes existed before because we chose M to be |
63 | // the smallest available hole larger than N. So, there is still no more than one hole of |
64 | // each size, and no hole larger than any hole that existed previously. |
65 | // 2. No hole equal or larger N exists. In that case we extend the data section's size by one |
66 | // word, creating a new 64-bit hole at the end. We then allocate N bits from it, creating |
67 | // a series of holes between N and 64 bits, as described in point (1). Thus, again, there |
68 | // is still at most one hole of each size, and the largest hole is 32 bits. |
69 | |
70 | UIntType holes[6]; |
71 | // The offset of each hole as a multiple of its size. A value of zero indicates that no hole |
72 | // exists. Notice that it is impossible for any actual hole to have an offset of zero, because |
73 | // the first field allocated is always placed at the very beginning of the section. So either |
74 | // the section has a size of zero (in which case there are no holes), or offset zero is |
75 | // already allocated and therefore cannot be a hole. |
76 | |
77 | kj::Maybe<UIntType> tryAllocate(UIntType lgSize) { |
78 | // Try to find space for a field of size 2^lgSize within the set of holes. If found, |
79 | // remove it from the holes, and return its offset (as a multiple of its size). If there |
80 | // is no such space, returns zero (no hole can be at offset zero, as explained above). |
81 | |
82 | if (lgSize >= kj::size(holes)) { |
83 | return nullptr; |
84 | } else if (holes[lgSize] != 0) { |
85 | UIntType result = holes[lgSize]; |
86 | holes[lgSize] = 0; |
87 | return result; |
88 | } else { |
89 | KJ_IF_MAYBE(next, tryAllocate(lgSize + 1)) { |
90 | UIntType result = *next * 2; |
91 | holes[lgSize] = result + 1; |
92 | return result; |
93 | } else { |
94 | return nullptr; |
95 | } |
96 | } |
97 | } |
98 | |
99 | uint assertHoleAndAllocate(UIntType lgSize) { |
100 | KJ_ASSERT(holes[lgSize] != 0); |
101 | uint result = holes[lgSize]; |
102 | holes[lgSize] = 0; |
103 | return result; |
104 | } |
105 | |
106 | void addHolesAtEnd(UIntType lgSize, UIntType offset, |
107 | UIntType limitLgSize = sizeof(HoleSet::holes) / sizeof(HoleSet::holes[0])) { |
108 | // Add new holes of progressively larger sizes in the range [lgSize, limitLgSize) starting |
109 | // from the given offset. The idea is that you just allocated an lgSize-sized field from |
110 | // an limitLgSize-sized space, such as a newly-added word on the end of the data segment. |
111 | |
112 | KJ_DREQUIRE(limitLgSize <= kj::size(holes)); |
113 | |
114 | while (lgSize < limitLgSize) { |
115 | KJ_DREQUIRE(holes[lgSize] == 0); |
116 | KJ_DREQUIRE(offset % 2 == 1); |
117 | holes[lgSize] = offset; |
118 | ++lgSize; |
119 | offset = (offset + 1) / 2; |
120 | } |
121 | } |
122 | |
123 | bool tryExpand(UIntType oldLgSize, uint oldOffset, uint expansionFactor) { |
124 | // Try to expand the value at the given location by combining it with subsequent holes, so |
125 | // as to expand the location to be 2^expansionFactor times the size that it started as. |
126 | // (In other words, the new lgSize is oldLgSize + expansionFactor.) |
127 | |
128 | if (expansionFactor == 0) { |
129 | // No expansion requested. |
130 | return true; |
131 | } |
132 | if (holes[oldLgSize] != oldOffset + 1) { |
133 | // The space immediately after the location is not a hole. |
134 | return false; |
135 | } |
136 | |
137 | // We can expand the location by one factor by combining it with a hole. Try to further |
138 | // expand from there to the number of factors requested. |
139 | if (tryExpand(oldLgSize + 1, oldOffset >> 1, expansionFactor - 1)) { |
140 | // Success. Consume the hole. |
141 | holes[oldLgSize] = 0; |
142 | return true; |
143 | } else { |
144 | return false; |
145 | } |
146 | } |
147 | |
148 | kj::Maybe<uint> smallestAtLeast(uint size) { |
149 | // Return the size of the smallest hole that is equal to or larger than the given size. |
150 | |
151 | for (uint i = size; i < kj::size(holes); i++) { |
152 | if (holes[i] != 0) { |
153 | return i; |
154 | } |
155 | } |
156 | return nullptr; |
157 | } |
158 | |
159 | uint getFirstWordUsed() { |
160 | // Computes the lg of the amount of space used in the first word of the section. |
161 | |
162 | // If there is a 32-bit hole with a 32-bit offset, no more than the first 32 bits are used. |
163 | // If no more than the first 32 bits are used, and there is a 16-bit hole with a 16-bit |
164 | // offset, then no more than the first 16 bits are used. And so on. |
165 | for (uint i = kj::size(holes); i > 0; i--) { |
166 | if (holes[i - 1] != 1) { |
167 | return i; |
168 | } |
169 | } |
170 | return 0; |
171 | } |
172 | }; |
173 | |
174 | struct StructOrGroup { |
175 | // Abstract interface for scopes in which fields can be added. |
176 | |
177 | virtual void addVoid() = 0; |
178 | virtual uint addData(uint lgSize) = 0; |
179 | virtual uint addPointer() = 0; |
180 | virtual bool tryExpandData(uint oldLgSize, uint oldOffset, uint expansionFactor) = 0; |
181 | // Try to expand the given previously-allocated space by 2^expansionFactor. Succeeds -- |
182 | // returning true -- if the following space happens to be empty, making this expansion possible. |
183 | // Otherwise, returns false. |
184 | }; |
185 | |
186 | struct Top: public StructOrGroup { |
187 | uint dataWordCount = 0; |
188 | uint pointerCount = 0; |
189 | // Size of the struct so far. |
190 | |
191 | HoleSet<uint> holes; |
192 | |
193 | void addVoid() override {} |
194 | |
195 | uint addData(uint lgSize) override { |
196 | KJ_IF_MAYBE(hole, holes.tryAllocate(lgSize)) { |
197 | return *hole; |
198 | } else { |
199 | uint offset = dataWordCount++ << (6 - lgSize); |
200 | holes.addHolesAtEnd(lgSize, offset + 1); |
201 | return offset; |
202 | } |
203 | } |
204 | |
205 | uint addPointer() override { |
206 | return pointerCount++; |
207 | } |
208 | |
209 | bool tryExpandData(uint oldLgSize, uint oldOffset, uint expansionFactor) override { |
210 | return holes.tryExpand(oldLgSize, oldOffset, expansionFactor); |
211 | } |
212 | |
213 | Top() = default; |
214 | KJ_DISALLOW_COPY(Top); |
215 | }; |
216 | |
217 | struct Union { |
218 | struct DataLocation { |
219 | uint lgSize; |
220 | uint offset; |
221 | |
222 | bool tryExpandTo(Union& u, uint newLgSize) { |
223 | if (newLgSize <= lgSize) { |
224 | return true; |
225 | } else if (u.parent.tryExpandData(lgSize, offset, newLgSize - lgSize)) { |
226 | offset >>= (newLgSize - lgSize); |
227 | lgSize = newLgSize; |
228 | return true; |
229 | } else { |
230 | return false; |
231 | } |
232 | } |
233 | }; |
234 | |
235 | StructOrGroup& parent; |
236 | uint groupCount = 0; |
237 | kj::Maybe<uint> discriminantOffset; |
238 | kj::Vector<DataLocation> dataLocations; |
239 | kj::Vector<uint> pointerLocations; |
240 | |
241 | inline Union(StructOrGroup& parent): parent(parent) {} |
242 | KJ_DISALLOW_COPY(Union); |
243 | |
244 | uint addNewDataLocation(uint lgSize) { |
245 | // Add a whole new data location to the union with the given size. |
246 | |
247 | uint offset = parent.addData(lgSize); |
248 | dataLocations.add(DataLocation { lgSize, offset }); |
249 | return offset; |
250 | } |
251 | |
252 | uint addNewPointerLocation() { |
253 | // Add a whole new pointer location to the union with the given size. |
254 | |
255 | return pointerLocations.add(parent.addPointer()); |
256 | } |
257 | |
258 | void newGroupAddingFirstMember() { |
259 | if (++groupCount == 2) { |
260 | addDiscriminant(); |
261 | } |
262 | } |
263 | |
264 | bool addDiscriminant() { |
265 | if (discriminantOffset == nullptr) { |
266 | discriminantOffset = parent.addData(4); // 2^4 = 16 bits |
267 | return true; |
268 | } else { |
269 | return false; |
270 | } |
271 | } |
272 | }; |
273 | |
274 | struct Group final: public StructOrGroup { |
275 | public: |
276 | class DataLocationUsage { |
277 | public: |
278 | DataLocationUsage(): isUsed(false) {} |
279 | explicit DataLocationUsage(uint lgSize): isUsed(true), lgSizeUsed(lgSize) {} |
280 | |
281 | kj::Maybe<uint> smallestHoleAtLeast(Union::DataLocation& location, uint lgSize) { |
282 | // Find the smallest single hole that is at least the given size. This is used to find the |
283 | // optimal place to allocate each field -- it is placed in the smallest slot where it fits, |
284 | // to reduce fragmentation. Returns the size of the hole, if found. |
285 | |
286 | if (!isUsed) { |
287 | // The location is effectively one big hole. |
288 | if (lgSize <= location.lgSize) { |
289 | return location.lgSize; |
290 | } else { |
291 | return nullptr; |
292 | } |
293 | } else if (lgSize >= lgSizeUsed) { |
294 | // Requested size is at least our current usage, so clearly won't fit in any current |
295 | // holes, but if the location's size is larger than what we're using, we'd be able to |
296 | // expand. |
297 | if (lgSize < location.lgSize) { |
298 | return lgSize; |
299 | } else { |
300 | return nullptr; |
301 | } |
302 | } else KJ_IF_MAYBE(result, holes.smallestAtLeast(lgSize)) { |
303 | // There's a hole. |
304 | return *result; |
305 | } else { |
306 | // The requested size is smaller than what we're already using, but there are no holes |
307 | // available. If we could double our size, then we could allocate in the new space. |
308 | |
309 | if (lgSizeUsed < location.lgSize) { |
310 | // We effectively create a new hole the same size as the current usage. |
311 | return lgSizeUsed; |
312 | } else { |
313 | return nullptr; |
314 | } |
315 | } |
316 | } |
317 | |
318 | uint allocateFromHole(Group& group, Union::DataLocation& location, uint lgSize) { |
319 | // Allocate the given space from an existing hole, given smallestHoleAtLeast() already |
320 | // returned non-null indicating such a hole exists. |
321 | |
322 | uint result; |
323 | |
324 | if (!isUsed) { |
325 | // The location is totally unused, so just allocate from the beginning. |
326 | KJ_DASSERT(lgSize <= location.lgSize, "Did smallestHoleAtLeast() really find a hole?" ); |
327 | result = 0; |
328 | isUsed = true; |
329 | lgSizeUsed = lgSize; |
330 | } else if (lgSize >= lgSizeUsed) { |
331 | // Requested size is at least our current usage, so clearly won't fit in any holes. |
332 | // We must expand to double the requested size, and return the second half. |
333 | KJ_DASSERT(lgSize < location.lgSize, "Did smallestHoleAtLeast() really find a hole?" ); |
334 | holes.addHolesAtEnd(lgSizeUsed, 1, lgSize); |
335 | lgSizeUsed = lgSize + 1; |
336 | result = 1; |
337 | } else KJ_IF_MAYBE(hole, holes.tryAllocate(lgSize)) { |
338 | // Found a hole. |
339 | result = *hole; |
340 | } else { |
341 | // The requested size is smaller than what we're using so far, but didn't fit in a |
342 | // hole. We should double our "used" size, then allocate from the new space. |
343 | KJ_DASSERT(lgSizeUsed < location.lgSize, |
344 | "Did smallestHoleAtLeast() really find a hole?" ); |
345 | result = 1 << (lgSizeUsed - lgSize); |
346 | holes.addHolesAtEnd(lgSize, result + 1, lgSizeUsed); |
347 | lgSizeUsed += 1; |
348 | } |
349 | |
350 | // Adjust the offset according to the location's offset before returning. |
351 | uint locationOffset = location.offset << (location.lgSize - lgSize); |
352 | return locationOffset + result; |
353 | } |
354 | |
355 | kj::Maybe<uint> tryAllocateByExpanding( |
356 | Group& group, Union::DataLocation& location, uint lgSize) { |
357 | // Attempt to allocate the given size by requesting that the parent union expand this |
358 | // location to fit. This is used if smallestHoleAtLeast() already determined that there |
359 | // are no holes that would fit, so we don't bother checking that. |
360 | |
361 | if (!isUsed) { |
362 | if (location.tryExpandTo(group.parent, lgSize)) { |
363 | isUsed = true; |
364 | lgSizeUsed = lgSize; |
365 | return location.offset << (location.lgSize - lgSize); |
366 | } else { |
367 | return nullptr; |
368 | } |
369 | } else { |
370 | uint newSize = kj::max(lgSizeUsed, lgSize) + 1; |
371 | if (tryExpandUsage(group, location, newSize, true)) { |
372 | uint result = KJ_ASSERT_NONNULL(holes.tryAllocate(lgSize)); |
373 | uint locationOffset = location.offset << (location.lgSize - lgSize); |
374 | return locationOffset + result; |
375 | } else { |
376 | return nullptr; |
377 | } |
378 | } |
379 | } |
380 | |
381 | bool tryExpand(Group& group, Union::DataLocation& location, |
382 | uint oldLgSize, uint oldOffset, uint expansionFactor) { |
383 | if (oldOffset == 0 && lgSizeUsed == oldLgSize) { |
384 | // This location contains exactly the requested data, so just expand the whole thing. |
385 | return tryExpandUsage(group, location, oldLgSize + expansionFactor, false); |
386 | } else { |
387 | // This location contains the requested data plus other stuff. Therefore the data cannot |
388 | // possibly expand past the end of the space we've already marked used without either |
389 | // overlapping with something else or breaking alignment rules. We only have to combine |
390 | // it with holes. |
391 | return holes.tryExpand(oldLgSize, oldOffset, expansionFactor); |
392 | } |
393 | } |
394 | |
395 | private: |
396 | bool isUsed; |
397 | // Whether or not this location has been used at all by the group. |
398 | |
399 | uint8_t lgSizeUsed; |
400 | // Amount of space from the location which is "used". This is the minimum size needed to |
401 | // cover all allocated space. Only meaningful if `isUsed` is true. |
402 | |
403 | HoleSet<uint8_t> holes; |
404 | // Indicates holes present in the space designated by `lgSizeUsed`. The offsets in this |
405 | // HoleSet are relative to the beginning of this particular data location, not the beginning |
406 | // of the struct. |
407 | |
408 | bool tryExpandUsage(Group& group, Union::DataLocation& location, uint desiredUsage, |
409 | bool newHoles) { |
410 | if (desiredUsage > location.lgSize) { |
411 | // Need to expand the underlying slot. |
412 | if (!location.tryExpandTo(group.parent, desiredUsage)) { |
413 | return false; |
414 | } |
415 | } |
416 | |
417 | // Underlying slot is big enough, so expand our size and update holes. |
418 | if (newHoles) { |
419 | holes.addHolesAtEnd(lgSizeUsed, 1, desiredUsage); |
420 | } else if (shouldDetectIssue344()) { |
421 | // Unfortunately, Cap'n Proto 0.5.x and below would always call addHolesAtEnd(), which |
422 | // was the wrong thing to do when called from tryExpand(), which itself is only called |
423 | // in cases involving unions nested in other unions. The bug could lead to multiple |
424 | // fields in a group incorrectly being assigned overlapping offsets. Although the bug |
425 | // is now fixed by adding the `newHoles` parameter, this silently breaks |
426 | // backwards-compatibilty with affected schemas. Therefore, for now, we throw an |
427 | // exception to alert developers of the problem. |
428 | // |
429 | // TODO(cleanup): Once sufficient time has elapsed, remove this assert. |
430 | KJ_FAIL_ASSERT("Bad news: Cap'n Proto 0.5.x and previous contained a bug which would cause this schema to be compiled incorrectly. Please see: https://github.com/sandstorm-io/capnproto/issues/344" ); |
431 | } |
432 | lgSizeUsed = desiredUsage; |
433 | return true; |
434 | } |
435 | }; |
436 | |
437 | Union& parent; |
438 | |
439 | kj::Vector<DataLocationUsage> parentDataLocationUsage; |
440 | // Vector corresponding to the parent union's `dataLocations`, indicating how much of each |
441 | // location has already been allocated. |
442 | |
443 | uint parentPointerLocationUsage = 0; |
444 | // Number of parent's pointer locations that have been used by this group. |
445 | |
446 | bool hasMembers = false; |
447 | |
448 | inline Group(Union& parent): parent(parent) {} |
449 | KJ_DISALLOW_COPY(Group); |
450 | |
451 | void addMember() { |
452 | if (!hasMembers) { |
453 | hasMembers = true; |
454 | parent.newGroupAddingFirstMember(); |
455 | } |
456 | } |
457 | |
458 | void addVoid() override { |
459 | addMember(); |
460 | |
461 | // Make sure that if this is a member of a union which is in turn a member of another union, |
462 | // that we let the outer union know that a field is being added, even though it is a |
463 | // zero-size field. This is important because the union needs to allocate its discriminant |
464 | // just before its second member is added. |
465 | parent.parent.addVoid(); |
466 | } |
467 | |
468 | uint addData(uint lgSize) override { |
469 | addMember(); |
470 | |
471 | uint bestSize = kj::maxValue; |
472 | kj::Maybe<uint> bestLocation = nullptr; |
473 | |
474 | for (uint i = 0; i < parent.dataLocations.size(); i++) { |
475 | // If we haven't seen this DataLocation yet, add a corresponding DataLocationUsage. |
476 | if (parentDataLocationUsage.size() == i) { |
477 | parentDataLocationUsage.add(); |
478 | } |
479 | |
480 | auto& usage = parentDataLocationUsage[i]; |
481 | KJ_IF_MAYBE(hole, usage.smallestHoleAtLeast(parent.dataLocations[i], lgSize)) { |
482 | if (*hole < bestSize) { |
483 | bestSize = *hole; |
484 | bestLocation = i; |
485 | } |
486 | } |
487 | } |
488 | |
489 | KJ_IF_MAYBE(best, bestLocation) { |
490 | return parentDataLocationUsage[*best].allocateFromHole( |
491 | *this, parent.dataLocations[*best], lgSize); |
492 | } |
493 | |
494 | // There are no holes at all in the union big enough to fit this field. Go back through all |
495 | // of the locations and attempt to expand them to fit. |
496 | for (uint i = 0; i < parent.dataLocations.size(); i++) { |
497 | KJ_IF_MAYBE(result, parentDataLocationUsage[i].tryAllocateByExpanding( |
498 | *this, parent.dataLocations[i], lgSize)) { |
499 | return *result; |
500 | } |
501 | } |
502 | |
503 | // Couldn't find any space in the existing locations, so add a new one. |
504 | uint result = parent.addNewDataLocation(lgSize); |
505 | parentDataLocationUsage.add(lgSize); |
506 | return result; |
507 | } |
508 | |
509 | uint addPointer() override { |
510 | addMember(); |
511 | |
512 | if (parentPointerLocationUsage < parent.pointerLocations.size()) { |
513 | return parent.pointerLocations[parentPointerLocationUsage++]; |
514 | } else { |
515 | parentPointerLocationUsage++; |
516 | return parent.addNewPointerLocation(); |
517 | } |
518 | } |
519 | |
520 | bool tryExpandData(uint oldLgSize, uint oldOffset, uint expansionFactor) override { |
521 | bool mustFail = false; |
522 | if (oldLgSize + expansionFactor > 6 || |
523 | (oldOffset & ((1 << expansionFactor) - 1)) != 0) { |
524 | // Expansion is not possible because the new size is too large or the offset is not |
525 | // properly-aligned. |
526 | |
527 | // Unfortunately, Cap'n Proto 0.5.x and prior forgot to "return false" here, instead |
528 | // continuing to execute the rest of the method. In most cases, the method failed later |
529 | // on, causing no harm. But, in cases where the method later succeeded, it probably |
530 | // led to bogus layouts. We cannot simply add the return statement now as this would |
531 | // silently break backwards-compatibility with affected schemas. Instead, we detect the |
532 | // problem and throw an exception. |
533 | // |
534 | // TODO(cleanup): Once sufficient time has elapsed, switch to "return false;" here. |
535 | if (shouldDetectIssue344()) { |
536 | mustFail = true; |
537 | } else { |
538 | return false; |
539 | } |
540 | } |
541 | |
542 | for (uint i = 0; i < parentDataLocationUsage.size(); i++) { |
543 | auto& location = parent.dataLocations[i]; |
544 | if (location.lgSize >= oldLgSize && |
545 | oldOffset >> (location.lgSize - oldLgSize) == location.offset) { |
546 | // The location we're trying to expand is a subset of this data location. |
547 | auto& usage = parentDataLocationUsage[i]; |
548 | |
549 | // Adjust the offset to be only within this location. |
550 | uint localOldOffset = oldOffset - (location.offset << (location.lgSize - oldLgSize)); |
551 | |
552 | // Try to expand. |
553 | bool result = usage.tryExpand( |
554 | *this, location, oldLgSize, localOldOffset, expansionFactor); |
555 | if (mustFail && result) { |
556 | KJ_FAIL_ASSERT("Bad news: Cap'n Proto 0.5.x and previous contained a bug which would cause this schema to be compiled incorrectly. Please see: https://github.com/sandstorm-io/capnproto/issues/344" ); |
557 | } |
558 | return result; |
559 | } |
560 | } |
561 | |
562 | KJ_FAIL_ASSERT("Tried to expand field that was never allocated." ); |
563 | return false; |
564 | } |
565 | }; |
566 | |
567 | Top& getTop() { return top; } |
568 | |
569 | private: |
570 | Top top; |
571 | }; |
572 | |
573 | // ======================================================================================= |
574 | |
575 | class NodeTranslator::BrandedDecl { |
576 | // Represents a declaration possibly with generic parameter bindings. |
577 | // |
578 | // TODO(cleaup): This is too complicated to live here. We should refactor this class and |
579 | // BrandScope out into their own file, independent of NodeTranslator. |
580 | |
581 | public: |
582 | inline BrandedDecl(Resolver::ResolvedDecl decl, |
583 | kj::Own<NodeTranslator::BrandScope>&& brand, |
584 | Expression::Reader source) |
585 | : brand(kj::mv(brand)), source(source) { |
586 | body.init<Resolver::ResolvedDecl>(kj::mv(decl)); |
587 | } |
588 | inline BrandedDecl(Resolver::ResolvedParameter variable, Expression::Reader source) |
589 | : source(source) { |
590 | body.init<Resolver::ResolvedParameter>(kj::mv(variable)); |
591 | } |
592 | inline BrandedDecl(decltype(nullptr)) {} |
593 | |
594 | static BrandedDecl implicitMethodParam(uint index) { |
595 | // Get a BrandedDecl referring to an implicit method parameter. |
596 | // (As a hack, we internally represent this as a ResolvedParameter. Sorry.) |
597 | return BrandedDecl(Resolver::ResolvedParameter { 0, index }, Expression::Reader()); |
598 | } |
599 | |
600 | BrandedDecl(BrandedDecl& other); |
601 | BrandedDecl(BrandedDecl&& other) = default; |
602 | |
603 | BrandedDecl& operator=(BrandedDecl& other); |
604 | BrandedDecl& operator=(BrandedDecl&& other) = default; |
605 | |
606 | // TODO(cleanup): A lot of the methods below are actually only called within compileAsType(), |
607 | // which was originally a method on NodeTranslator, but now is a method here and thus doesn't |
608 | // need these to be public. We should privatize most of these. |
609 | |
610 | kj::Maybe<BrandedDecl> applyParams(kj::Array<BrandedDecl> params, Expression::Reader subSource); |
611 | // Treat the declaration as a generic and apply it to the given parameter list. |
612 | |
613 | kj::Maybe<BrandedDecl> getMember(kj::StringPtr memberName, Expression::Reader subSource); |
614 | // Get a member of this declaration. |
615 | |
616 | kj::Maybe<Declaration::Which> getKind(); |
617 | // Returns the kind of declaration, or null if this is an unbound generic variable. |
618 | |
619 | template <typename InitBrandFunc> |
620 | uint64_t getIdAndFillBrand(InitBrandFunc&& initBrand); |
621 | // Returns the type ID of this node. `initBrand` is a zero-arg functor which returns |
622 | // schema::Brand::Builder; this will be called if this decl has brand bindings, and |
623 | // the returned builder filled in to reflect those bindings. |
624 | // |
625 | // It is an error to call this when `getKind()` returns null. |
626 | |
627 | kj::Maybe<BrandedDecl&> getListParam(); |
628 | // Only if the kind is BUILTIN_LIST: Get the list's type parameter. |
629 | |
630 | Resolver::ResolvedParameter asVariable(); |
631 | // If this is an unbound generic variable (i.e. `getKind()` returns null), return information |
632 | // about the variable. |
633 | // |
634 | // It is an error to call this when `getKind()` does not return null. |
635 | |
636 | bool compileAsType(ErrorReporter& errorReporter, schema::Type::Builder target); |
637 | // Compile this decl to a schema::Type. |
638 | |
639 | inline void addError(ErrorReporter& errorReporter, kj::StringPtr message) { |
640 | errorReporter.addErrorOn(source, message); |
641 | } |
642 | |
643 | Resolver::ResolveResult asResolveResult(uint64_t scopeId, schema::Brand::Builder brandBuilder); |
644 | // Reverse this into a ResolveResult. If necessary, use `brandBuilder` to fill in |
645 | // ResolvedDecl.brand. |
646 | |
647 | kj::String toString(); |
648 | kj::String toDebugString(); |
649 | |
650 | private: |
651 | Resolver::ResolveResult body; |
652 | kj::Own<NodeTranslator::BrandScope> brand; // null if parameter |
653 | Expression::Reader source; |
654 | }; |
655 | |
656 | class NodeTranslator::BrandScope: public kj::Refcounted { |
657 | // Tracks the brand parameter bindings affecting the current scope. For example, if we are |
658 | // interpreting the type expression "Foo(Text).Bar", we would start with the current scopes |
659 | // BrandScope, create a new child BrandScope representing "Foo", add the "(Text)" parameter |
660 | // bindings to it, then create a further child scope for "Bar". Thus the BrandScope for Bar |
661 | // knows that Foo's parameter list has been bound to "(Text)". |
662 | // |
663 | // TODO(cleanup): This is too complicated to live here. We should refactor this class and |
664 | // BrandedDecl out into their own file, independent of NodeTranslator. |
665 | |
666 | public: |
667 | BrandScope(ErrorReporter& errorReporter, uint64_t startingScopeId, |
668 | uint startingScopeParamCount, Resolver& startingScope) |
669 | : errorReporter(errorReporter), parent(nullptr), leafId(startingScopeId), |
670 | leafParamCount(startingScopeParamCount), inherited(true) { |
671 | // Create all lexical parent scopes, all with no brand bindings. |
672 | KJ_IF_MAYBE(p, startingScope.getParent()) { |
673 | parent = kj::refcounted<BrandScope>( |
674 | errorReporter, p->id, p->genericParamCount, *p->resolver); |
675 | } |
676 | } |
677 | |
678 | bool isGeneric() { |
679 | if (leafParamCount > 0) return true; |
680 | |
681 | KJ_IF_MAYBE(p, parent) { |
682 | return p->get()->isGeneric(); |
683 | } else { |
684 | return false; |
685 | } |
686 | } |
687 | |
688 | kj::Own<BrandScope> push(uint64_t typeId, uint paramCount) { |
689 | return kj::refcounted<BrandScope>(kj::addRef(*this), typeId, paramCount); |
690 | } |
691 | |
692 | kj::Maybe<kj::Own<BrandScope>> setParams( |
693 | kj::Array<BrandedDecl> params, Declaration::Which genericType, Expression::Reader source) { |
694 | if (this->params.size() != 0) { |
695 | errorReporter.addErrorOn(source, "Double-application of generic parameters." ); |
696 | return nullptr; |
697 | } else if (params.size() > leafParamCount) { |
698 | if (leafParamCount == 0) { |
699 | errorReporter.addErrorOn(source, "Declaration does not accept generic parameters." ); |
700 | } else { |
701 | errorReporter.addErrorOn(source, "Too many generic parameters." ); |
702 | } |
703 | return nullptr; |
704 | } else if (params.size() < leafParamCount) { |
705 | errorReporter.addErrorOn(source, "Not enough generic parameters." ); |
706 | return nullptr; |
707 | } else { |
708 | if (genericType != Declaration::BUILTIN_LIST) { |
709 | for (auto& param: params) { |
710 | KJ_IF_MAYBE(kind, param.getKind()) { |
711 | switch (*kind) { |
712 | case Declaration::BUILTIN_LIST: |
713 | case Declaration::BUILTIN_TEXT: |
714 | case Declaration::BUILTIN_DATA: |
715 | case Declaration::BUILTIN_ANY_POINTER: |
716 | case Declaration::STRUCT: |
717 | case Declaration::INTERFACE: |
718 | break; |
719 | |
720 | default: |
721 | param.addError(errorReporter, |
722 | "Sorry, only pointer types can be used as generic parameters." ); |
723 | break; |
724 | } |
725 | } |
726 | } |
727 | } |
728 | |
729 | return kj::refcounted<BrandScope>(*this, kj::mv(params)); |
730 | } |
731 | } |
732 | |
733 | kj::Own<BrandScope> pop(uint64_t newLeafId) { |
734 | if (leafId == newLeafId) { |
735 | return kj::addRef(*this); |
736 | } |
737 | KJ_IF_MAYBE(p, parent) { |
738 | return (*p)->pop(newLeafId); |
739 | } else { |
740 | // Looks like we're moving into a whole top-level scope. |
741 | return kj::refcounted<BrandScope>(errorReporter, newLeafId); |
742 | } |
743 | } |
744 | |
745 | kj::Maybe<BrandedDecl> lookupParameter(Resolver& resolver, uint64_t scopeId, uint index) { |
746 | // Returns null if the param should be inherited from the client scope. |
747 | |
748 | if (scopeId == leafId) { |
749 | if (index < params.size()) { |
750 | return params[index]; |
751 | } else if (inherited) { |
752 | return nullptr; |
753 | } else { |
754 | // Unbound and not inherited, so return AnyPointer. |
755 | auto decl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER); |
756 | return BrandedDecl(decl, |
757 | evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()), |
758 | Expression::Reader()); |
759 | } |
760 | } else KJ_IF_MAYBE(p, parent) { |
761 | return p->get()->lookupParameter(resolver, scopeId, index); |
762 | } else { |
763 | KJ_FAIL_REQUIRE("scope is not a parent" ); |
764 | } |
765 | } |
766 | |
767 | kj::Maybe<kj::ArrayPtr<BrandedDecl>> getParams(uint64_t scopeId) { |
768 | // Returns null if params at the requested scope should be inherited from the client scope. |
769 | |
770 | if (scopeId == leafId) { |
771 | if (inherited) { |
772 | return nullptr; |
773 | } else { |
774 | return params.asPtr(); |
775 | } |
776 | } else KJ_IF_MAYBE(p, parent) { |
777 | return p->get()->getParams(scopeId); |
778 | } else { |
779 | KJ_FAIL_REQUIRE("scope is not a parent" ); |
780 | } |
781 | } |
782 | |
783 | template <typename InitBrandFunc> |
784 | void compile(InitBrandFunc&& initBrand) { |
785 | kj::Vector<BrandScope*> levels; |
786 | BrandScope* ptr = this; |
787 | for (;;) { |
788 | if (ptr->params.size() > 0 || (ptr->inherited && ptr->leafParamCount > 0)) { |
789 | levels.add(ptr); |
790 | } |
791 | KJ_IF_MAYBE(p, ptr->parent) { |
792 | ptr = *p; |
793 | } else { |
794 | break; |
795 | } |
796 | } |
797 | |
798 | if (levels.size() > 0) { |
799 | auto scopes = initBrand().initScopes(levels.size()); |
800 | for (uint i: kj::indices(levels)) { |
801 | auto scope = scopes[i]; |
802 | scope.setScopeId(levels[i]->leafId); |
803 | |
804 | if (levels[i]->inherited) { |
805 | scope.setInherit(); |
806 | } else { |
807 | auto bindings = scope.initBind(levels[i]->params.size()); |
808 | for (uint j: kj::indices(bindings)) { |
809 | levels[i]->params[j].compileAsType(errorReporter, bindings[j].initType()); |
810 | } |
811 | } |
812 | } |
813 | } |
814 | } |
815 | |
816 | kj::Maybe<NodeTranslator::BrandedDecl> compileDeclExpression( |
817 | Expression::Reader source, Resolver& resolver, |
818 | ImplicitParams implicitMethodParams); |
819 | |
820 | NodeTranslator::BrandedDecl interpretResolve( |
821 | Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source); |
822 | |
823 | kj::Own<NodeTranslator::BrandScope> evaluateBrand( |
824 | Resolver& resolver, Resolver::ResolvedDecl decl, |
825 | List<schema::Brand::Scope>::Reader brand, uint index = 0); |
826 | |
827 | BrandedDecl decompileType(Resolver& resolver, schema::Type::Reader type); |
828 | |
829 | inline uint64_t getScopeId() { return leafId; } |
830 | |
831 | private: |
832 | ErrorReporter& errorReporter; |
833 | kj::Maybe<kj::Own<NodeTranslator::BrandScope>> parent; |
834 | uint64_t leafId; // zero = this is the root |
835 | uint leafParamCount; // number of generic parameters on this leaf |
836 | bool inherited; |
837 | kj::Array<BrandedDecl> params; |
838 | |
839 | BrandScope(kj::Own<NodeTranslator::BrandScope> parent, uint64_t leafId, uint leafParamCount) |
840 | : errorReporter(parent->errorReporter), |
841 | parent(kj::mv(parent)), leafId(leafId), leafParamCount(leafParamCount), |
842 | inherited(false) {} |
843 | BrandScope(BrandScope& base, kj::Array<BrandedDecl> params) |
844 | : errorReporter(base.errorReporter), |
845 | leafId(base.leafId), leafParamCount(base.leafParamCount), |
846 | inherited(false), params(kj::mv(params)) { |
847 | KJ_IF_MAYBE(p, base.parent) { |
848 | parent = kj::addRef(**p); |
849 | } |
850 | } |
851 | BrandScope(ErrorReporter& errorReporter, uint64_t scopeId) |
852 | : errorReporter(errorReporter), leafId(scopeId), leafParamCount(0), inherited(false) {} |
853 | |
854 | template <typename T, typename... Params> |
855 | friend kj::Own<T> kj::refcounted(Params&&... params); |
856 | }; |
857 | |
858 | NodeTranslator::BrandedDecl::BrandedDecl(BrandedDecl& other) |
859 | : body(other.body), |
860 | source(other.source) { |
861 | if (body.is<Resolver::ResolvedDecl>()) { |
862 | brand = kj::addRef(*other.brand); |
863 | } |
864 | } |
865 | |
866 | NodeTranslator::BrandedDecl& NodeTranslator::BrandedDecl::operator=(BrandedDecl& other) { |
867 | body = other.body; |
868 | source = other.source; |
869 | if (body.is<Resolver::ResolvedDecl>()) { |
870 | brand = kj::addRef(*other.brand); |
871 | } |
872 | return *this; |
873 | } |
874 | |
875 | kj::Maybe<NodeTranslator::BrandedDecl> NodeTranslator::BrandedDecl::applyParams( |
876 | kj::Array<BrandedDecl> params, Expression::Reader subSource) { |
877 | if (body.is<Resolver::ResolvedParameter>()) { |
878 | return nullptr; |
879 | } else { |
880 | return brand->setParams(kj::mv(params), body.get<Resolver::ResolvedDecl>().kind, subSource) |
881 | .map([&](kj::Own<BrandScope>&& scope) { |
882 | BrandedDecl result = *this; |
883 | result.brand = kj::mv(scope); |
884 | result.source = subSource; |
885 | return result; |
886 | }); |
887 | } |
888 | } |
889 | |
890 | kj::Maybe<NodeTranslator::BrandedDecl> NodeTranslator::BrandedDecl::getMember( |
891 | kj::StringPtr memberName, Expression::Reader subSource) { |
892 | if (body.is<Resolver::ResolvedParameter>()) { |
893 | return nullptr; |
894 | } else KJ_IF_MAYBE(r, body.get<Resolver::ResolvedDecl>().resolver->resolveMember(memberName)) { |
895 | return brand->interpretResolve(*body.get<Resolver::ResolvedDecl>().resolver, *r, subSource); |
896 | } else { |
897 | return nullptr; |
898 | } |
899 | } |
900 | |
901 | kj::Maybe<Declaration::Which> NodeTranslator::BrandedDecl::getKind() { |
902 | if (body.is<Resolver::ResolvedParameter>()) { |
903 | return nullptr; |
904 | } else { |
905 | return body.get<Resolver::ResolvedDecl>().kind; |
906 | } |
907 | } |
908 | |
909 | template <typename InitBrandFunc> |
910 | uint64_t NodeTranslator::BrandedDecl::getIdAndFillBrand(InitBrandFunc&& initBrand) { |
911 | KJ_REQUIRE(body.is<Resolver::ResolvedDecl>()); |
912 | |
913 | brand->compile(kj::fwd<InitBrandFunc>(initBrand)); |
914 | return body.get<Resolver::ResolvedDecl>().id; |
915 | } |
916 | |
917 | kj::Maybe<NodeTranslator::BrandedDecl&> NodeTranslator::BrandedDecl::getListParam() { |
918 | KJ_REQUIRE(body.is<Resolver::ResolvedDecl>()); |
919 | |
920 | auto& decl = body.get<Resolver::ResolvedDecl>(); |
921 | KJ_REQUIRE(decl.kind == Declaration::BUILTIN_LIST); |
922 | |
923 | auto params = KJ_ASSERT_NONNULL(brand->getParams(decl.id)); |
924 | if (params.size() != 1) { |
925 | return nullptr; |
926 | } else { |
927 | return params[0]; |
928 | } |
929 | } |
930 | |
931 | NodeTranslator::Resolver::ResolvedParameter NodeTranslator::BrandedDecl::asVariable() { |
932 | KJ_REQUIRE(body.is<Resolver::ResolvedParameter>()); |
933 | |
934 | return body.get<Resolver::ResolvedParameter>(); |
935 | } |
936 | |
937 | bool NodeTranslator::BrandedDecl::compileAsType( |
938 | ErrorReporter& errorReporter, schema::Type::Builder target) { |
939 | KJ_IF_MAYBE(kind, getKind()) { |
940 | switch (*kind) { |
941 | case Declaration::ENUM: { |
942 | auto enum_ = target.initEnum(); |
943 | enum_.setTypeId(getIdAndFillBrand([&]() { return enum_.initBrand(); })); |
944 | return true; |
945 | } |
946 | |
947 | case Declaration::STRUCT: { |
948 | auto struct_ = target.initStruct(); |
949 | struct_.setTypeId(getIdAndFillBrand([&]() { return struct_.initBrand(); })); |
950 | return true; |
951 | } |
952 | |
953 | case Declaration::INTERFACE: { |
954 | auto interface = target.initInterface(); |
955 | interface.setTypeId(getIdAndFillBrand([&]() { return interface.initBrand(); })); |
956 | return true; |
957 | } |
958 | |
959 | case Declaration::BUILTIN_LIST: { |
960 | auto elementType = target.initList().initElementType(); |
961 | |
962 | KJ_IF_MAYBE(param, getListParam()) { |
963 | if (!param->compileAsType(errorReporter, elementType)) { |
964 | return false; |
965 | } |
966 | } else { |
967 | addError(errorReporter, "'List' requires exactly one parameter." ); |
968 | return false; |
969 | } |
970 | |
971 | if (elementType.isAnyPointer()) { |
972 | addError(errorReporter, "'List(AnyPointer)' is not supported." ); |
973 | // Seeing List(AnyPointer) later can mess things up, so change the type to Void. |
974 | elementType.setVoid(); |
975 | return false; |
976 | } |
977 | |
978 | return true; |
979 | } |
980 | |
981 | case Declaration::BUILTIN_VOID: target.setVoid(); return true; |
982 | case Declaration::BUILTIN_BOOL: target.setBool(); return true; |
983 | case Declaration::BUILTIN_INT8: target.setInt8(); return true; |
984 | case Declaration::BUILTIN_INT16: target.setInt16(); return true; |
985 | case Declaration::BUILTIN_INT32: target.setInt32(); return true; |
986 | case Declaration::BUILTIN_INT64: target.setInt64(); return true; |
987 | case Declaration::BUILTIN_U_INT8: target.setUint8(); return true; |
988 | case Declaration::BUILTIN_U_INT16: target.setUint16(); return true; |
989 | case Declaration::BUILTIN_U_INT32: target.setUint32(); return true; |
990 | case Declaration::BUILTIN_U_INT64: target.setUint64(); return true; |
991 | case Declaration::BUILTIN_FLOAT32: target.setFloat32(); return true; |
992 | case Declaration::BUILTIN_FLOAT64: target.setFloat64(); return true; |
993 | case Declaration::BUILTIN_TEXT: target.setText(); return true; |
994 | case Declaration::BUILTIN_DATA: target.setData(); return true; |
995 | |
996 | case Declaration::BUILTIN_OBJECT: |
997 | addError(errorReporter, |
998 | "As of Cap'n Proto 0.4, 'Object' has been renamed to 'AnyPointer'. Sorry for the " |
999 | "inconvenience, and thanks for being an early adopter. :)" ); |
1000 | // fallthrough |
1001 | case Declaration::BUILTIN_ANY_POINTER: |
1002 | target.initAnyPointer().initUnconstrained().setAnyKind(); |
1003 | return true; |
1004 | case Declaration::BUILTIN_ANY_STRUCT: |
1005 | target.initAnyPointer().initUnconstrained().setStruct(); |
1006 | return true; |
1007 | case Declaration::BUILTIN_ANY_LIST: |
1008 | target.initAnyPointer().initUnconstrained().setList(); |
1009 | return true; |
1010 | case Declaration::BUILTIN_CAPABILITY: |
1011 | target.initAnyPointer().initUnconstrained().setCapability(); |
1012 | return true; |
1013 | |
1014 | case Declaration::FILE: |
1015 | case Declaration::USING: |
1016 | case Declaration::CONST: |
1017 | case Declaration::ENUMERANT: |
1018 | case Declaration::FIELD: |
1019 | case Declaration::UNION: |
1020 | case Declaration::GROUP: |
1021 | case Declaration::METHOD: |
1022 | case Declaration::ANNOTATION: |
1023 | case Declaration::NAKED_ID: |
1024 | case Declaration::NAKED_ANNOTATION: |
1025 | addError(errorReporter, kj::str("'" , toString(), "' is not a type." )); |
1026 | return false; |
1027 | } |
1028 | |
1029 | KJ_UNREACHABLE; |
1030 | } else { |
1031 | // Oh, this is a type variable. |
1032 | auto var = asVariable(); |
1033 | if (var.id == 0) { |
1034 | // This is actually a method implicit parameter. |
1035 | auto builder = target.initAnyPointer().initImplicitMethodParameter(); |
1036 | builder.setParameterIndex(var.index); |
1037 | return true; |
1038 | } else { |
1039 | auto builder = target.initAnyPointer().initParameter(); |
1040 | builder.setScopeId(var.id); |
1041 | builder.setParameterIndex(var.index); |
1042 | return true; |
1043 | } |
1044 | } |
1045 | } |
1046 | |
1047 | NodeTranslator::Resolver::ResolveResult NodeTranslator::BrandedDecl::asResolveResult( |
1048 | uint64_t scopeId, schema::Brand::Builder brandBuilder) { |
1049 | auto result = body; |
1050 | if (result.is<Resolver::ResolvedDecl>()) { |
1051 | // May need to compile our context as the "brand". |
1052 | |
1053 | result.get<Resolver::ResolvedDecl>().scopeId = scopeId; |
1054 | |
1055 | getIdAndFillBrand([&]() { |
1056 | result.get<Resolver::ResolvedDecl>().brand = brandBuilder.asReader(); |
1057 | return brandBuilder; |
1058 | }); |
1059 | } |
1060 | return result; |
1061 | } |
1062 | |
1063 | static kj::String expressionString(Expression::Reader name); // defined later |
1064 | |
1065 | kj::String NodeTranslator::BrandedDecl::toString() { |
1066 | return expressionString(source); |
1067 | } |
1068 | |
1069 | kj::String NodeTranslator::BrandedDecl::toDebugString() { |
1070 | if (body.is<Resolver::ResolvedParameter>()) { |
1071 | auto variable = body.get<Resolver::ResolvedParameter>(); |
1072 | return kj::str("variable(" , variable.id, ", " , variable.index, ")" ); |
1073 | } else { |
1074 | auto decl = body.get<Resolver::ResolvedDecl>(); |
1075 | return kj::str("decl(" , decl.id, ", " , (uint)decl.kind, "')" ); |
1076 | } |
1077 | } |
1078 | |
1079 | NodeTranslator::BrandedDecl NodeTranslator::BrandScope::interpretResolve( |
1080 | Resolver& resolver, Resolver::ResolveResult& result, Expression::Reader source) { |
1081 | if (result.is<Resolver::ResolvedDecl>()) { |
1082 | auto& decl = result.get<Resolver::ResolvedDecl>(); |
1083 | |
1084 | auto scope = pop(decl.scopeId); |
1085 | KJ_IF_MAYBE(brand, decl.brand) { |
1086 | scope = scope->evaluateBrand(resolver, decl, brand->getScopes()); |
1087 | } else { |
1088 | scope = scope->push(decl.id, decl.genericParamCount); |
1089 | } |
1090 | |
1091 | return BrandedDecl(decl, kj::mv(scope), source); |
1092 | } else { |
1093 | auto& param = result.get<Resolver::ResolvedParameter>(); |
1094 | KJ_IF_MAYBE(p, lookupParameter(resolver, param.id, param.index)) { |
1095 | return *p; |
1096 | } else { |
1097 | return BrandedDecl(param, source); |
1098 | } |
1099 | } |
1100 | } |
1101 | |
1102 | kj::Own<NodeTranslator::BrandScope> NodeTranslator::BrandScope::evaluateBrand( |
1103 | Resolver& resolver, Resolver::ResolvedDecl decl, |
1104 | List<schema::Brand::Scope>::Reader brand, uint index) { |
1105 | auto result = kj::refcounted<BrandScope>(errorReporter, decl.id); |
1106 | result->leafParamCount = decl.genericParamCount; |
1107 | |
1108 | // Fill in `params`. |
1109 | if (index < brand.size()) { |
1110 | auto nextScope = brand[index]; |
1111 | if (decl.id == nextScope.getScopeId()) { |
1112 | // Initialize our parameters. |
1113 | |
1114 | switch (nextScope.which()) { |
1115 | case schema::Brand::Scope::BIND: { |
1116 | auto bindings = nextScope.getBind(); |
1117 | auto params = kj::heapArrayBuilder<BrandedDecl>(bindings.size()); |
1118 | for (auto binding: bindings) { |
1119 | switch (binding.which()) { |
1120 | case schema::Brand::Binding::UNBOUND: { |
1121 | // Build an AnyPointer-equivalent. |
1122 | auto anyPointerDecl = resolver.resolveBuiltin(Declaration::BUILTIN_ANY_POINTER); |
1123 | params.add(BrandedDecl(anyPointerDecl, |
1124 | kj::refcounted<BrandScope>(errorReporter, anyPointerDecl.scopeId), |
1125 | Expression::Reader())); |
1126 | break; |
1127 | } |
1128 | |
1129 | case schema::Brand::Binding::TYPE: |
1130 | // Reverse this schema::Type back into a BrandedDecl. |
1131 | params.add(decompileType(resolver, binding.getType())); |
1132 | break; |
1133 | } |
1134 | } |
1135 | result->params = params.finish(); |
1136 | break; |
1137 | } |
1138 | |
1139 | case schema::Brand::Scope::INHERIT: |
1140 | KJ_IF_MAYBE(p, getParams(decl.id)) { |
1141 | result->params = kj::heapArray(*p); |
1142 | } else { |
1143 | result->inherited = true; |
1144 | } |
1145 | break; |
1146 | } |
1147 | |
1148 | // Parent should start one level deeper in the list. |
1149 | ++index; |
1150 | } |
1151 | } |
1152 | |
1153 | // Fill in `parent`. |
1154 | KJ_IF_MAYBE(parent, decl.resolver->getParent()) { |
1155 | result->parent = evaluateBrand(resolver, *parent, brand, index); |
1156 | } |
1157 | |
1158 | return result; |
1159 | } |
1160 | |
1161 | NodeTranslator::BrandedDecl NodeTranslator::BrandScope::decompileType( |
1162 | Resolver& resolver, schema::Type::Reader type) { |
1163 | auto builtin = [&](Declaration::Which which) -> BrandedDecl { |
1164 | auto decl = resolver.resolveBuiltin(which); |
1165 | return BrandedDecl(decl, |
1166 | evaluateBrand(resolver, decl, List<schema::Brand::Scope>::Reader()), |
1167 | Expression::Reader()); |
1168 | }; |
1169 | |
1170 | switch (type.which()) { |
1171 | case schema::Type::VOID: return builtin(Declaration::BUILTIN_VOID); |
1172 | case schema::Type::BOOL: return builtin(Declaration::BUILTIN_BOOL); |
1173 | case schema::Type::INT8: return builtin(Declaration::BUILTIN_INT8); |
1174 | case schema::Type::INT16: return builtin(Declaration::BUILTIN_INT16); |
1175 | case schema::Type::INT32: return builtin(Declaration::BUILTIN_INT32); |
1176 | case schema::Type::INT64: return builtin(Declaration::BUILTIN_INT64); |
1177 | case schema::Type::UINT8: return builtin(Declaration::BUILTIN_U_INT8); |
1178 | case schema::Type::UINT16: return builtin(Declaration::BUILTIN_U_INT16); |
1179 | case schema::Type::UINT32: return builtin(Declaration::BUILTIN_U_INT32); |
1180 | case schema::Type::UINT64: return builtin(Declaration::BUILTIN_U_INT64); |
1181 | case schema::Type::FLOAT32: return builtin(Declaration::BUILTIN_FLOAT32); |
1182 | case schema::Type::FLOAT64: return builtin(Declaration::BUILTIN_FLOAT64); |
1183 | case schema::Type::TEXT: return builtin(Declaration::BUILTIN_TEXT); |
1184 | case schema::Type::DATA: return builtin(Declaration::BUILTIN_DATA); |
1185 | |
1186 | case schema::Type::ENUM: { |
1187 | auto enumType = type.getEnum(); |
1188 | Resolver::ResolvedDecl decl = resolver.resolveId(enumType.getTypeId()); |
1189 | return BrandedDecl(decl, |
1190 | evaluateBrand(resolver, decl, enumType.getBrand().getScopes()), |
1191 | Expression::Reader()); |
1192 | } |
1193 | |
1194 | case schema::Type::INTERFACE: { |
1195 | auto interfaceType = type.getInterface(); |
1196 | Resolver::ResolvedDecl decl = resolver.resolveId(interfaceType.getTypeId()); |
1197 | return BrandedDecl(decl, |
1198 | evaluateBrand(resolver, decl, interfaceType.getBrand().getScopes()), |
1199 | Expression::Reader()); |
1200 | } |
1201 | |
1202 | case schema::Type::STRUCT: { |
1203 | auto structType = type.getStruct(); |
1204 | Resolver::ResolvedDecl decl = resolver.resolveId(structType.getTypeId()); |
1205 | return BrandedDecl(decl, |
1206 | evaluateBrand(resolver, decl, structType.getBrand().getScopes()), |
1207 | Expression::Reader()); |
1208 | } |
1209 | |
1210 | case schema::Type::LIST: { |
1211 | auto elementType = decompileType(resolver, type.getList().getElementType()); |
1212 | return KJ_ASSERT_NONNULL(builtin(Declaration::BUILTIN_LIST) |
1213 | .applyParams(kj::heapArray(&elementType, 1), Expression::Reader())); |
1214 | } |
1215 | |
1216 | case schema::Type::ANY_POINTER: { |
1217 | auto anyPointer = type.getAnyPointer(); |
1218 | switch (anyPointer.which()) { |
1219 | case schema::Type::AnyPointer::UNCONSTRAINED: |
1220 | return builtin(Declaration::BUILTIN_ANY_POINTER); |
1221 | |
1222 | case schema::Type::AnyPointer::PARAMETER: { |
1223 | auto param = anyPointer.getParameter(); |
1224 | auto id = param.getScopeId(); |
1225 | uint index = param.getParameterIndex(); |
1226 | KJ_IF_MAYBE(binding, lookupParameter(resolver, id, index)) { |
1227 | return *binding; |
1228 | } else { |
1229 | return BrandedDecl(Resolver::ResolvedParameter {id, index}, Expression::Reader()); |
1230 | } |
1231 | } |
1232 | |
1233 | case schema::Type::AnyPointer::IMPLICIT_METHOD_PARAMETER: |
1234 | KJ_FAIL_ASSERT("Alias pointed to implicit method type parameter?" ); |
1235 | } |
1236 | |
1237 | KJ_UNREACHABLE; |
1238 | } |
1239 | } |
1240 | |
1241 | KJ_UNREACHABLE; |
1242 | } |
1243 | |
1244 | kj::Maybe<NodeTranslator::BrandedDecl> NodeTranslator::BrandScope::compileDeclExpression( |
1245 | Expression::Reader source, Resolver& resolver, |
1246 | ImplicitParams implicitMethodParams) { |
1247 | switch (source.which()) { |
1248 | case Expression::UNKNOWN: |
1249 | // Error reported earlier. |
1250 | return nullptr; |
1251 | |
1252 | case Expression::POSITIVE_INT: |
1253 | case Expression::NEGATIVE_INT: |
1254 | case Expression::FLOAT: |
1255 | case Expression::STRING: |
1256 | case Expression::BINARY: |
1257 | case Expression::LIST: |
1258 | case Expression::TUPLE: |
1259 | case Expression::EMBED: |
1260 | errorReporter.addErrorOn(source, "Expected name." ); |
1261 | return nullptr; |
1262 | |
1263 | case Expression::RELATIVE_NAME: { |
1264 | auto name = source.getRelativeName(); |
1265 | auto nameValue = name.getValue(); |
1266 | |
1267 | // Check implicit method params first. |
1268 | for (auto i: kj::indices(implicitMethodParams.params)) { |
1269 | if (implicitMethodParams.params[i].getName() == nameValue) { |
1270 | if (implicitMethodParams.scopeId == 0) { |
1271 | return BrandedDecl::implicitMethodParam(i); |
1272 | } else { |
1273 | return BrandedDecl(Resolver::ResolvedParameter { |
1274 | implicitMethodParams.scopeId, static_cast<uint16_t>(i) }, |
1275 | Expression::Reader()); |
1276 | } |
1277 | } |
1278 | } |
1279 | |
1280 | KJ_IF_MAYBE(r, resolver.resolve(nameValue)) { |
1281 | return interpretResolve(resolver, *r, source); |
1282 | } else { |
1283 | errorReporter.addErrorOn(name, kj::str("Not defined: " , nameValue)); |
1284 | return nullptr; |
1285 | } |
1286 | } |
1287 | |
1288 | case Expression::ABSOLUTE_NAME: { |
1289 | auto name = source.getAbsoluteName(); |
1290 | KJ_IF_MAYBE(r, resolver.getTopScope().resolver->resolveMember(name.getValue())) { |
1291 | return interpretResolve(resolver, *r, source); |
1292 | } else { |
1293 | errorReporter.addErrorOn(name, kj::str("Not defined: " , name.getValue())); |
1294 | return nullptr; |
1295 | } |
1296 | } |
1297 | |
1298 | case Expression::IMPORT: { |
1299 | auto filename = source.getImport(); |
1300 | KJ_IF_MAYBE(decl, resolver.resolveImport(filename.getValue())) { |
1301 | // Import is always a root scope, so create a fresh BrandScope. |
1302 | return BrandedDecl(*decl, kj::refcounted<BrandScope>( |
1303 | errorReporter, decl->id, decl->genericParamCount, *decl->resolver), source); |
1304 | } else { |
1305 | errorReporter.addErrorOn(filename, kj::str("Import failed: " , filename.getValue())); |
1306 | return nullptr; |
1307 | } |
1308 | } |
1309 | |
1310 | case Expression::APPLICATION: { |
1311 | auto app = source.getApplication(); |
1312 | KJ_IF_MAYBE(decl, compileDeclExpression(app.getFunction(), resolver, implicitMethodParams)) { |
1313 | // Compile all params. |
1314 | auto params = app.getParams(); |
1315 | auto compiledParams = kj::heapArrayBuilder<BrandedDecl>(params.size()); |
1316 | bool paramFailed = false; |
1317 | for (auto param: params) { |
1318 | if (param.isNamed()) { |
1319 | errorReporter.addErrorOn(param.getNamed(), "Named parameter not allowed here." ); |
1320 | } |
1321 | |
1322 | KJ_IF_MAYBE(d, compileDeclExpression(param.getValue(), resolver, implicitMethodParams)) { |
1323 | compiledParams.add(kj::mv(*d)); |
1324 | } else { |
1325 | // Param failed to compile. Error was already reported. |
1326 | paramFailed = true; |
1327 | } |
1328 | }; |
1329 | |
1330 | if (paramFailed) { |
1331 | return kj::mv(*decl); |
1332 | } |
1333 | |
1334 | // Add the parameters to the brand. |
1335 | KJ_IF_MAYBE(applied, decl->applyParams(compiledParams.finish(), source)) { |
1336 | return kj::mv(*applied); |
1337 | } else { |
1338 | // Error already reported. Ignore parameters. |
1339 | return kj::mv(*decl); |
1340 | } |
1341 | } else { |
1342 | // error already reported |
1343 | return nullptr; |
1344 | } |
1345 | } |
1346 | |
1347 | case Expression::MEMBER: { |
1348 | auto member = source.getMember(); |
1349 | KJ_IF_MAYBE(decl, compileDeclExpression(member.getParent(), resolver, implicitMethodParams)) { |
1350 | auto name = member.getName(); |
1351 | KJ_IF_MAYBE(memberDecl, decl->getMember(name.getValue(), source)) { |
1352 | return kj::mv(*memberDecl); |
1353 | } else { |
1354 | errorReporter.addErrorOn(name, kj::str( |
1355 | "'" , expressionString(member.getParent()), |
1356 | "' has no member named '" , name.getValue(), "'" )); |
1357 | return nullptr; |
1358 | } |
1359 | } else { |
1360 | // error already reported |
1361 | return nullptr; |
1362 | } |
1363 | } |
1364 | } |
1365 | |
1366 | KJ_UNREACHABLE; |
1367 | } |
1368 | |
1369 | // ======================================================================================= |
1370 | |
1371 | NodeTranslator::NodeTranslator( |
1372 | Resolver& resolver, ErrorReporter& errorReporter, |
1373 | const Declaration::Reader& decl, Orphan<schema::Node> wipNodeParam, |
1374 | bool compileAnnotations) |
1375 | : resolver(resolver), errorReporter(errorReporter), |
1376 | orphanage(Orphanage::getForMessageContaining(wipNodeParam.get())), |
1377 | compileAnnotations(compileAnnotations), |
1378 | localBrand(kj::refcounted<BrandScope>( |
1379 | errorReporter, wipNodeParam.getReader().getId(), |
1380 | decl.getParameters().size(), resolver)), |
1381 | wipNode(kj::mv(wipNodeParam)), |
1382 | sourceInfo(orphanage.newOrphan<schema::Node::SourceInfo>()) { |
1383 | compileNode(decl, wipNode.get()); |
1384 | } |
1385 | |
1386 | NodeTranslator::~NodeTranslator() noexcept(false) {} |
1387 | |
1388 | NodeTranslator::NodeSet NodeTranslator::getBootstrapNode() { |
1389 | auto sourceInfos = kj::heapArrayBuilder<schema::Node::SourceInfo::Reader>( |
1390 | 1 + groups.size() + paramStructs.size()); |
1391 | sourceInfos.add(sourceInfo.getReader()); |
1392 | for (auto& group: groups) { |
1393 | sourceInfos.add(group.sourceInfo.getReader()); |
1394 | } |
1395 | for (auto& paramStruct: paramStructs) { |
1396 | sourceInfos.add(paramStruct.sourceInfo.getReader()); |
1397 | } |
1398 | |
1399 | auto nodeReader = wipNode.getReader(); |
1400 | if (nodeReader.isInterface()) { |
1401 | return NodeSet { |
1402 | nodeReader, |
1403 | KJ_MAP(g, paramStructs) { return g.node.getReader(); }, |
1404 | sourceInfos.finish() |
1405 | }; |
1406 | } else { |
1407 | return NodeSet { |
1408 | nodeReader, |
1409 | KJ_MAP(g, groups) { return g.node.getReader(); }, |
1410 | sourceInfos.finish() |
1411 | }; |
1412 | } |
1413 | } |
1414 | |
1415 | NodeTranslator::NodeSet NodeTranslator::finish() { |
1416 | // Careful about iteration here: compileFinalValue() may actually add more elements to |
1417 | // `unfinishedValues`, invalidating iterators in the process. |
1418 | for (size_t i = 0; i < unfinishedValues.size(); i++) { |
1419 | auto& value = unfinishedValues[i]; |
1420 | compileValue(value.source, value.type, value.typeScope, value.target, false); |
1421 | } |
1422 | |
1423 | return getBootstrapNode(); |
1424 | } |
1425 | |
1426 | class NodeTranslator::DuplicateNameDetector { |
1427 | public: |
1428 | inline explicit DuplicateNameDetector(ErrorReporter& errorReporter) |
1429 | : errorReporter(errorReporter) {} |
1430 | void check(List<Declaration>::Reader nestedDecls, Declaration::Which parentKind); |
1431 | |
1432 | private: |
1433 | ErrorReporter& errorReporter; |
1434 | std::map<kj::StringPtr, LocatedText::Reader> names; |
1435 | }; |
1436 | |
1437 | void NodeTranslator::compileNode(Declaration::Reader decl, schema::Node::Builder builder) { |
1438 | DuplicateNameDetector(errorReporter) |
1439 | .check(decl.getNestedDecls(), decl.which()); |
1440 | |
1441 | auto genericParams = decl.getParameters(); |
1442 | if (genericParams.size() > 0) { |
1443 | auto paramsBuilder = builder.initParameters(genericParams.size()); |
1444 | for (auto i: kj::indices(genericParams)) { |
1445 | paramsBuilder[i].setName(genericParams[i].getName()); |
1446 | } |
1447 | } |
1448 | |
1449 | builder.setIsGeneric(localBrand->isGeneric()); |
1450 | |
1451 | kj::StringPtr targetsFlagName; |
1452 | |
1453 | switch (decl.which()) { |
1454 | case Declaration::FILE: |
1455 | targetsFlagName = "targetsFile" ; |
1456 | break; |
1457 | case Declaration::CONST: |
1458 | compileConst(decl.getConst(), builder.initConst()); |
1459 | targetsFlagName = "targetsConst" ; |
1460 | break; |
1461 | case Declaration::ANNOTATION: |
1462 | compileAnnotation(decl.getAnnotation(), builder.initAnnotation()); |
1463 | targetsFlagName = "targetsAnnotation" ; |
1464 | break; |
1465 | case Declaration::ENUM: |
1466 | compileEnum(decl.getEnum(), decl.getNestedDecls(), builder); |
1467 | targetsFlagName = "targetsEnum" ; |
1468 | break; |
1469 | case Declaration::STRUCT: |
1470 | compileStruct(decl.getStruct(), decl.getNestedDecls(), builder); |
1471 | targetsFlagName = "targetsStruct" ; |
1472 | break; |
1473 | case Declaration::INTERFACE: |
1474 | compileInterface(decl.getInterface(), decl.getNestedDecls(), builder); |
1475 | targetsFlagName = "targetsInterface" ; |
1476 | break; |
1477 | |
1478 | default: |
1479 | KJ_FAIL_REQUIRE("This Declaration is not a node." ); |
1480 | break; |
1481 | } |
1482 | |
1483 | builder.adoptAnnotations(compileAnnotationApplications(decl.getAnnotations(), targetsFlagName)); |
1484 | |
1485 | auto di = sourceInfo.get(); |
1486 | di.setId(wipNode.getReader().getId()); |
1487 | if (decl.hasDocComment()) { |
1488 | di.setDocComment(decl.getDocComment()); |
1489 | } |
1490 | } |
1491 | |
1492 | static kj::StringPtr getExpressionTargetName(Expression::Reader exp) { |
1493 | kj::StringPtr targetName; |
1494 | switch (exp.which()) { |
1495 | case Expression::ABSOLUTE_NAME: |
1496 | return exp.getAbsoluteName().getValue(); |
1497 | case Expression::RELATIVE_NAME: |
1498 | return exp.getRelativeName().getValue(); |
1499 | case Expression::APPLICATION: |
1500 | return getExpressionTargetName(exp.getApplication().getFunction()); |
1501 | case Expression::MEMBER: |
1502 | return exp.getMember().getName().getValue(); |
1503 | default: |
1504 | return nullptr; |
1505 | } |
1506 | } |
1507 | |
1508 | void NodeTranslator::DuplicateNameDetector::check( |
1509 | List<Declaration>::Reader nestedDecls, Declaration::Which parentKind) { |
1510 | for (auto decl: nestedDecls) { |
1511 | { |
1512 | auto name = decl.getName(); |
1513 | auto nameText = name.getValue(); |
1514 | auto insertResult = names.insert(std::make_pair(nameText, name)); |
1515 | if (!insertResult.second) { |
1516 | if (nameText.size() == 0 && decl.isUnion()) { |
1517 | errorReporter.addErrorOn( |
1518 | name, kj::str("An unnamed union is already defined in this scope." )); |
1519 | errorReporter.addErrorOn( |
1520 | insertResult.first->second, kj::str("Previously defined here." )); |
1521 | } else { |
1522 | errorReporter.addErrorOn( |
1523 | name, kj::str("'" , nameText, "' is already defined in this scope." )); |
1524 | errorReporter.addErrorOn( |
1525 | insertResult.first->second, kj::str("'" , nameText, "' previously defined here." )); |
1526 | } |
1527 | } |
1528 | |
1529 | switch (decl.which()) { |
1530 | case Declaration::USING: { |
1531 | kj::StringPtr targetName = getExpressionTargetName(decl.getUsing().getTarget()); |
1532 | if (targetName.size() > 0 && targetName[0] >= 'a' && targetName[0] <= 'z') { |
1533 | // Target starts with lower-case letter, so alias should too. |
1534 | if (nameText.size() > 0 && (nameText[0] < 'a' || nameText[0] > 'z')) { |
1535 | errorReporter.addErrorOn(name, |
1536 | "Non-type names must begin with a lower-case letter." ); |
1537 | } |
1538 | } else { |
1539 | // Target starts with capital or is not named (probably, an import). Require |
1540 | // capitalization. |
1541 | if (nameText.size() > 0 && (nameText[0] < 'A' || nameText[0] > 'Z')) { |
1542 | errorReporter.addErrorOn(name, |
1543 | "Type names must begin with a capital letter." ); |
1544 | } |
1545 | } |
1546 | break; |
1547 | } |
1548 | |
1549 | case Declaration::ENUM: |
1550 | case Declaration::STRUCT: |
1551 | case Declaration::INTERFACE: |
1552 | if (nameText.size() > 0 && (nameText[0] < 'A' || nameText[0] > 'Z')) { |
1553 | errorReporter.addErrorOn(name, |
1554 | "Type names must begin with a capital letter." ); |
1555 | } |
1556 | break; |
1557 | |
1558 | case Declaration::CONST: |
1559 | case Declaration::ANNOTATION: |
1560 | case Declaration::ENUMERANT: |
1561 | case Declaration::METHOD: |
1562 | case Declaration::FIELD: |
1563 | case Declaration::UNION: |
1564 | case Declaration::GROUP: |
1565 | if (nameText.size() > 0 && (nameText[0] < 'a' || nameText[0] > 'z')) { |
1566 | errorReporter.addErrorOn(name, |
1567 | "Non-type names must begin with a lower-case letter." ); |
1568 | } |
1569 | break; |
1570 | |
1571 | default: |
1572 | KJ_ASSERT(nameText.size() == 0, "Don't know what naming rules to enforce for node type." , |
1573 | (uint)decl.which()); |
1574 | break; |
1575 | } |
1576 | |
1577 | if (nameText.findFirst('_') != nullptr) { |
1578 | errorReporter.addErrorOn(name, |
1579 | "Cap'n Proto declaration names should use camelCase and must not contain " |
1580 | "underscores. (Code generators may convert names to the appropriate style for the " |
1581 | "target language.)" ); |
1582 | } |
1583 | } |
1584 | |
1585 | switch (decl.which()) { |
1586 | case Declaration::USING: |
1587 | case Declaration::CONST: |
1588 | case Declaration::ENUM: |
1589 | case Declaration::STRUCT: |
1590 | case Declaration::INTERFACE: |
1591 | case Declaration::ANNOTATION: |
1592 | switch (parentKind) { |
1593 | case Declaration::FILE: |
1594 | case Declaration::STRUCT: |
1595 | case Declaration::INTERFACE: |
1596 | // OK. |
1597 | break; |
1598 | default: |
1599 | errorReporter.addErrorOn(decl, "This kind of declaration doesn't belong here." ); |
1600 | break; |
1601 | } |
1602 | break; |
1603 | |
1604 | case Declaration::ENUMERANT: |
1605 | if (parentKind != Declaration::ENUM) { |
1606 | errorReporter.addErrorOn(decl, "Enumerants can only appear in enums." ); |
1607 | } |
1608 | break; |
1609 | case Declaration::METHOD: |
1610 | if (parentKind != Declaration::INTERFACE) { |
1611 | errorReporter.addErrorOn(decl, "Methods can only appear in interfaces." ); |
1612 | } |
1613 | break; |
1614 | case Declaration::FIELD: |
1615 | case Declaration::UNION: |
1616 | case Declaration::GROUP: |
1617 | switch (parentKind) { |
1618 | case Declaration::STRUCT: |
1619 | case Declaration::UNION: |
1620 | case Declaration::GROUP: |
1621 | // OK. |
1622 | break; |
1623 | default: |
1624 | errorReporter.addErrorOn(decl, "This declaration can only appear in structs." ); |
1625 | break; |
1626 | } |
1627 | |
1628 | // Struct members may have nested decls. We need to check those here, because no one else |
1629 | // is going to do it. |
1630 | if (decl.getName().getValue().size() == 0) { |
1631 | // Unnamed union. Check members as if they are in the same scope. |
1632 | check(decl.getNestedDecls(), decl.which()); |
1633 | } else { |
1634 | // Children are in their own scope. |
1635 | DuplicateNameDetector(errorReporter) |
1636 | .check(decl.getNestedDecls(), decl.which()); |
1637 | } |
1638 | |
1639 | break; |
1640 | |
1641 | default: |
1642 | errorReporter.addErrorOn(decl, "This kind of declaration doesn't belong here." ); |
1643 | break; |
1644 | } |
1645 | } |
1646 | } |
1647 | |
1648 | void NodeTranslator::compileConst(Declaration::Const::Reader decl, |
1649 | schema::Node::Const::Builder builder) { |
1650 | auto typeBuilder = builder.initType(); |
1651 | if (compileType(decl.getType(), typeBuilder, noImplicitParams())) { |
1652 | compileBootstrapValue(decl.getValue(), typeBuilder.asReader(), builder.initValue()); |
1653 | } |
1654 | } |
1655 | |
1656 | void NodeTranslator::compileAnnotation(Declaration::Annotation::Reader decl, |
1657 | schema::Node::Annotation::Builder builder) { |
1658 | compileType(decl.getType(), builder.initType(), noImplicitParams()); |
1659 | |
1660 | // Dynamically copy over the values of all of the "targets" members. |
1661 | DynamicStruct::Reader src = decl; |
1662 | DynamicStruct::Builder dst = builder; |
1663 | for (auto srcField: src.getSchema().getFields()) { |
1664 | kj::StringPtr fieldName = srcField.getProto().getName(); |
1665 | if (fieldName.startsWith("targets" )) { |
1666 | auto dstField = dst.getSchema().getFieldByName(fieldName); |
1667 | dst.set(dstField, src.get(srcField)); |
1668 | } |
1669 | } |
1670 | } |
1671 | |
1672 | class NodeTranslator::DuplicateOrdinalDetector { |
1673 | public: |
1674 | DuplicateOrdinalDetector(ErrorReporter& errorReporter): errorReporter(errorReporter) {} |
1675 | |
1676 | void check(LocatedInteger::Reader ordinal) { |
1677 | if (ordinal.getValue() < expectedOrdinal) { |
1678 | errorReporter.addErrorOn(ordinal, "Duplicate ordinal number." ); |
1679 | KJ_IF_MAYBE(last, lastOrdinalLocation) { |
1680 | errorReporter.addErrorOn( |
1681 | *last, kj::str("Ordinal @" , last->getValue(), " originally used here." )); |
1682 | // Don't report original again. |
1683 | lastOrdinalLocation = nullptr; |
1684 | } |
1685 | } else if (ordinal.getValue() > expectedOrdinal) { |
1686 | errorReporter.addErrorOn(ordinal, |
1687 | kj::str("Skipped ordinal @" , expectedOrdinal, ". Ordinals must be sequential with no " |
1688 | "holes." )); |
1689 | expectedOrdinal = ordinal.getValue() + 1; |
1690 | } else { |
1691 | ++expectedOrdinal; |
1692 | lastOrdinalLocation = ordinal; |
1693 | } |
1694 | } |
1695 | |
1696 | private: |
1697 | ErrorReporter& errorReporter; |
1698 | uint expectedOrdinal = 0; |
1699 | kj::Maybe<LocatedInteger::Reader> lastOrdinalLocation; |
1700 | }; |
1701 | |
1702 | void NodeTranslator::compileEnum(Void decl, |
1703 | List<Declaration>::Reader members, |
1704 | schema::Node::Builder builder) { |
1705 | // maps ordinal -> (code order, declaration) |
1706 | std::multimap<uint, std::pair<uint, Declaration::Reader>> enumerants; |
1707 | |
1708 | uint codeOrder = 0; |
1709 | for (auto member: members) { |
1710 | if (member.isEnumerant()) { |
1711 | enumerants.insert( |
1712 | std::make_pair(member.getId().getOrdinal().getValue(), |
1713 | std::make_pair(codeOrder++, member))); |
1714 | } |
1715 | } |
1716 | |
1717 | auto list = builder.initEnum().initEnumerants(enumerants.size()); |
1718 | auto sourceInfoList = sourceInfo.get().initMembers(enumerants.size()); |
1719 | uint i = 0; |
1720 | DuplicateOrdinalDetector dupDetector(errorReporter); |
1721 | |
1722 | for (auto& entry: enumerants) { |
1723 | uint codeOrder = entry.second.first; |
1724 | Declaration::Reader enumerantDecl = entry.second.second; |
1725 | |
1726 | dupDetector.check(enumerantDecl.getId().getOrdinal()); |
1727 | |
1728 | if (enumerantDecl.hasDocComment()) { |
1729 | sourceInfoList[i].setDocComment(enumerantDecl.getDocComment()); |
1730 | } |
1731 | |
1732 | auto enumerantBuilder = list[i++]; |
1733 | enumerantBuilder.setName(enumerantDecl.getName().getValue()); |
1734 | enumerantBuilder.setCodeOrder(codeOrder); |
1735 | enumerantBuilder.adoptAnnotations(compileAnnotationApplications( |
1736 | enumerantDecl.getAnnotations(), "targetsEnumerant" )); |
1737 | } |
1738 | } |
1739 | |
1740 | // ------------------------------------------------------------------- |
1741 | |
1742 | class NodeTranslator::StructTranslator { |
1743 | public: |
1744 | explicit StructTranslator(NodeTranslator& translator, ImplicitParams implicitMethodParams) |
1745 | : translator(translator), errorReporter(translator.errorReporter), |
1746 | implicitMethodParams(implicitMethodParams) {} |
1747 | KJ_DISALLOW_COPY(StructTranslator); |
1748 | |
1749 | void translate(Void decl, List<Declaration>::Reader members, schema::Node::Builder builder, |
1750 | schema::Node::SourceInfo::Builder sourceInfo) { |
1751 | // Build the member-info-by-ordinal map. |
1752 | MemberInfo root(builder, sourceInfo); |
1753 | traverseTopOrGroup(members, root, layout.getTop()); |
1754 | translateInternal(root, builder); |
1755 | } |
1756 | |
1757 | void translate(List<Declaration::Param>::Reader params, schema::Node::Builder builder, |
1758 | schema::Node::SourceInfo::Builder sourceInfo) { |
1759 | // Build a struct from a method param / result list. |
1760 | MemberInfo root(builder, sourceInfo); |
1761 | traverseParams(params, root, layout.getTop()); |
1762 | translateInternal(root, builder); |
1763 | } |
1764 | |
1765 | private: |
1766 | NodeTranslator& translator; |
1767 | ErrorReporter& errorReporter; |
1768 | ImplicitParams implicitMethodParams; |
1769 | StructLayout layout; |
1770 | kj::Arena arena; |
1771 | |
1772 | struct NodeSourceInfoBuilderPair { |
1773 | schema::Node::Builder node; |
1774 | schema::Node::SourceInfo::Builder sourceInfo; |
1775 | }; |
1776 | |
1777 | struct FieldSourceInfoBuilderPair { |
1778 | schema::Field::Builder field; |
1779 | schema::Node::SourceInfo::Member::Builder sourceInfo; |
1780 | }; |
1781 | |
1782 | struct MemberInfo { |
1783 | MemberInfo* parent; |
1784 | // The MemberInfo for the parent scope. |
1785 | |
1786 | uint codeOrder; |
1787 | // Code order within the parent. |
1788 | |
1789 | uint index = 0; |
1790 | // Index within the parent. |
1791 | |
1792 | uint childCount = 0; |
1793 | // Number of children this member has. |
1794 | |
1795 | uint childInitializedCount = 0; |
1796 | // Number of children whose `schema` member has been initialized. This initialization happens |
1797 | // while walking the fields in ordinal order. |
1798 | |
1799 | uint unionDiscriminantCount = 0; |
1800 | // Number of children who are members of the scope's union and have had their discriminant |
1801 | // value decided. |
1802 | |
1803 | bool isInUnion; |
1804 | // Whether or not this field is in the parent's union. |
1805 | |
1806 | kj::StringPtr name; |
1807 | Declaration::Id::Reader declId; |
1808 | Declaration::Which declKind; |
1809 | bool isParam = false; |
1810 | bool hasDefaultValue = false; // if declKind == FIELD |
1811 | Expression::Reader fieldType; // if declKind == FIELD |
1812 | Expression::Reader fieldDefaultValue; // if declKind == FIELD && hasDefaultValue |
1813 | List<Declaration::AnnotationApplication>::Reader declAnnotations; |
1814 | uint startByte = 0; |
1815 | uint endByte = 0; |
1816 | // Information about the field declaration. We don't use Declaration::Reader because it might |
1817 | // have come from a Declaration::Param instead. |
1818 | |
1819 | kj::Maybe<Text::Reader> = nullptr; |
1820 | |
1821 | kj::Maybe<schema::Field::Builder> schema; |
1822 | // Schema for the field. Initialized when getSchema() is first called. |
1823 | |
1824 | schema::Node::Builder node; |
1825 | schema::Node::SourceInfo::Builder sourceInfo; |
1826 | // If it's a group, or the top-level struct. |
1827 | |
1828 | union { |
1829 | StructLayout::StructOrGroup* fieldScope; |
1830 | // If this member is a field, the scope of that field. This will be used to assign an |
1831 | // offset for the field when going through in ordinal order. |
1832 | |
1833 | StructLayout::Union* unionScope; |
1834 | // If this member is a union, or it is a group or top-level struct containing an unnamed |
1835 | // union, this is the union. This will be used to assign a discriminant offset when the |
1836 | // union's ordinal comes up (if the union has an explicit ordinal), as well as to finally |
1837 | // copy over the discriminant offset to the schema. |
1838 | }; |
1839 | |
1840 | inline explicit MemberInfo(schema::Node::Builder node, |
1841 | schema::Node::SourceInfo::Builder sourceInfo) |
1842 | : parent(nullptr), codeOrder(0), isInUnion(false), node(node), sourceInfo(sourceInfo), |
1843 | unionScope(nullptr) {} |
1844 | inline MemberInfo(MemberInfo& parent, uint codeOrder, |
1845 | const Declaration::Reader& decl, |
1846 | StructLayout::StructOrGroup& fieldScope, |
1847 | bool isInUnion) |
1848 | : parent(&parent), codeOrder(codeOrder), isInUnion(isInUnion), |
1849 | name(decl.getName().getValue()), declId(decl.getId()), declKind(Declaration::FIELD), |
1850 | declAnnotations(decl.getAnnotations()), |
1851 | startByte(decl.getStartByte()), endByte(decl.getEndByte()), |
1852 | node(nullptr), sourceInfo(nullptr), fieldScope(&fieldScope) { |
1853 | KJ_REQUIRE(decl.which() == Declaration::FIELD); |
1854 | auto fieldDecl = decl.getField(); |
1855 | fieldType = fieldDecl.getType(); |
1856 | if (fieldDecl.getDefaultValue().isValue()) { |
1857 | hasDefaultValue = true; |
1858 | fieldDefaultValue = fieldDecl.getDefaultValue().getValue(); |
1859 | } |
1860 | if (decl.hasDocComment()) { |
1861 | docComment = decl.getDocComment(); |
1862 | } |
1863 | } |
1864 | inline MemberInfo(MemberInfo& parent, uint codeOrder, |
1865 | const Declaration::Param::Reader& decl, |
1866 | StructLayout::StructOrGroup& fieldScope, |
1867 | bool isInUnion) |
1868 | : parent(&parent), codeOrder(codeOrder), isInUnion(isInUnion), |
1869 | name(decl.getName().getValue()), declKind(Declaration::FIELD), isParam(true), |
1870 | declAnnotations(decl.getAnnotations()), |
1871 | startByte(decl.getStartByte()), endByte(decl.getEndByte()), |
1872 | node(nullptr), sourceInfo(nullptr), fieldScope(&fieldScope) { |
1873 | fieldType = decl.getType(); |
1874 | if (decl.getDefaultValue().isValue()) { |
1875 | hasDefaultValue = true; |
1876 | fieldDefaultValue = decl.getDefaultValue().getValue(); |
1877 | } |
1878 | } |
1879 | inline MemberInfo(MemberInfo& parent, uint codeOrder, |
1880 | const Declaration::Reader& decl, |
1881 | NodeSourceInfoBuilderPair builderPair, |
1882 | bool isInUnion) |
1883 | : parent(&parent), codeOrder(codeOrder), isInUnion(isInUnion), |
1884 | name(decl.getName().getValue()), declId(decl.getId()), declKind(decl.which()), |
1885 | declAnnotations(decl.getAnnotations()), |
1886 | startByte(decl.getStartByte()), endByte(decl.getEndByte()), |
1887 | node(builderPair.node), sourceInfo(builderPair.sourceInfo), unionScope(nullptr) { |
1888 | KJ_REQUIRE(decl.which() != Declaration::FIELD); |
1889 | if (decl.hasDocComment()) { |
1890 | docComment = decl.getDocComment(); |
1891 | } |
1892 | } |
1893 | |
1894 | schema::Field::Builder getSchema() { |
1895 | KJ_IF_MAYBE(result, schema) { |
1896 | return *result; |
1897 | } else { |
1898 | index = parent->childInitializedCount; |
1899 | auto builderPair = parent->addMemberSchema(); |
1900 | auto builder = builderPair.field; |
1901 | if (isInUnion) { |
1902 | builder.setDiscriminantValue(parent->unionDiscriminantCount++); |
1903 | } |
1904 | builder.setName(name); |
1905 | builder.setCodeOrder(codeOrder); |
1906 | |
1907 | KJ_IF_MAYBE(dc, docComment) { |
1908 | builderPair.sourceInfo.setDocComment(*dc); |
1909 | } |
1910 | |
1911 | schema = builder; |
1912 | return builder; |
1913 | } |
1914 | } |
1915 | |
1916 | FieldSourceInfoBuilderPair addMemberSchema() { |
1917 | // Get the schema builder for the child member at the given index. This lazily/dynamically |
1918 | // builds the builder tree. |
1919 | |
1920 | KJ_REQUIRE(childInitializedCount < childCount); |
1921 | |
1922 | auto structNode = node.getStruct(); |
1923 | if (!structNode.hasFields()) { |
1924 | if (parent != nullptr) { |
1925 | getSchema(); // Make sure field exists in parent once the first child is added. |
1926 | } |
1927 | FieldSourceInfoBuilderPair result { |
1928 | structNode.initFields(childCount)[childInitializedCount], |
1929 | sourceInfo.initMembers(childCount)[childInitializedCount] |
1930 | }; |
1931 | ++childInitializedCount; |
1932 | return result; |
1933 | } else { |
1934 | FieldSourceInfoBuilderPair result { |
1935 | structNode.getFields()[childInitializedCount], |
1936 | sourceInfo.getMembers()[childInitializedCount] |
1937 | }; |
1938 | ++childInitializedCount; |
1939 | return result; |
1940 | } |
1941 | } |
1942 | |
1943 | void finishGroup() { |
1944 | if (unionScope != nullptr) { |
1945 | unionScope->addDiscriminant(); // if it hasn't happened already |
1946 | auto structNode = node.getStruct(); |
1947 | structNode.setDiscriminantCount(unionDiscriminantCount); |
1948 | structNode.setDiscriminantOffset(KJ_ASSERT_NONNULL(unionScope->discriminantOffset)); |
1949 | } |
1950 | |
1951 | if (parent != nullptr) { |
1952 | uint64_t groupId = generateGroupId(parent->node.getId(), index); |
1953 | node.setId(groupId); |
1954 | node.setScopeId(parent->node.getId()); |
1955 | getSchema().initGroup().setTypeId(groupId); |
1956 | |
1957 | sourceInfo.setId(groupId); |
1958 | KJ_IF_MAYBE(dc, docComment) { |
1959 | sourceInfo.setDocComment(*dc); |
1960 | } |
1961 | } |
1962 | } |
1963 | }; |
1964 | |
1965 | std::multimap<uint, MemberInfo*> membersByOrdinal; |
1966 | // Every member that has an explicit ordinal goes into this map. We then iterate over the map |
1967 | // to assign field offsets (or discriminant offsets for unions). |
1968 | |
1969 | kj::Vector<MemberInfo*> allMembers; |
1970 | // All members, including ones that don't have ordinals. |
1971 | |
1972 | void traverseUnion(const Declaration::Reader& decl, |
1973 | List<Declaration>::Reader members, MemberInfo& parent, |
1974 | StructLayout::Union& layout, uint& codeOrder) { |
1975 | if (members.size() < 2) { |
1976 | errorReporter.addErrorOn(decl, "Union must have at least two members." ); |
1977 | } |
1978 | |
1979 | for (auto member: members) { |
1980 | kj::Maybe<uint> ordinal; |
1981 | MemberInfo* memberInfo = nullptr; |
1982 | |
1983 | switch (member.which()) { |
1984 | case Declaration::FIELD: { |
1985 | parent.childCount++; |
1986 | // For layout purposes, pretend this field is enclosed in a one-member group. |
1987 | StructLayout::Group& singletonGroup = |
1988 | arena.allocate<StructLayout::Group>(layout); |
1989 | memberInfo = &arena.allocate<MemberInfo>(parent, codeOrder++, member, singletonGroup, |
1990 | true); |
1991 | allMembers.add(memberInfo); |
1992 | ordinal = member.getId().getOrdinal().getValue(); |
1993 | break; |
1994 | } |
1995 | |
1996 | case Declaration::UNION: |
1997 | if (member.getName().getValue() == "" ) { |
1998 | errorReporter.addErrorOn(member, "Unions cannot contain unnamed unions." ); |
1999 | } else { |
2000 | parent.childCount++; |
2001 | |
2002 | // For layout purposes, pretend this union is enclosed in a one-member group. |
2003 | StructLayout::Group& singletonGroup = |
2004 | arena.allocate<StructLayout::Group>(layout); |
2005 | StructLayout::Union& unionLayout = arena.allocate<StructLayout::Union>(singletonGroup); |
2006 | |
2007 | memberInfo = &arena.allocate<MemberInfo>( |
2008 | parent, codeOrder++, member, |
2009 | newGroupNode(parent.node, member.getName().getValue()), |
2010 | true); |
2011 | allMembers.add(memberInfo); |
2012 | memberInfo->unionScope = &unionLayout; |
2013 | uint subCodeOrder = 0; |
2014 | traverseUnion(member, member.getNestedDecls(), *memberInfo, unionLayout, subCodeOrder); |
2015 | if (member.getId().isOrdinal()) { |
2016 | ordinal = member.getId().getOrdinal().getValue(); |
2017 | } |
2018 | } |
2019 | break; |
2020 | |
2021 | case Declaration::GROUP: { |
2022 | parent.childCount++; |
2023 | StructLayout::Group& group = arena.allocate<StructLayout::Group>(layout); |
2024 | memberInfo = &arena.allocate<MemberInfo>( |
2025 | parent, codeOrder++, member, |
2026 | newGroupNode(parent.node, member.getName().getValue()), |
2027 | true); |
2028 | allMembers.add(memberInfo); |
2029 | traverseGroup(member.getNestedDecls(), *memberInfo, group); |
2030 | break; |
2031 | } |
2032 | |
2033 | default: |
2034 | // Ignore others. |
2035 | break; |
2036 | } |
2037 | |
2038 | KJ_IF_MAYBE(o, ordinal) { |
2039 | membersByOrdinal.insert(std::make_pair(*o, memberInfo)); |
2040 | } |
2041 | } |
2042 | } |
2043 | |
2044 | void traverseGroup(List<Declaration>::Reader members, MemberInfo& parent, |
2045 | StructLayout::StructOrGroup& layout) { |
2046 | if (members.size() < 1) { |
2047 | errorReporter.addError(parent.startByte, parent.endByte, |
2048 | "Group must have at least one member." ); |
2049 | } |
2050 | |
2051 | traverseTopOrGroup(members, parent, layout); |
2052 | } |
2053 | |
2054 | void traverseTopOrGroup(List<Declaration>::Reader members, MemberInfo& parent, |
2055 | StructLayout::StructOrGroup& layout) { |
2056 | uint codeOrder = 0; |
2057 | |
2058 | for (auto member: members) { |
2059 | kj::Maybe<uint> ordinal; |
2060 | MemberInfo* memberInfo = nullptr; |
2061 | |
2062 | switch (member.which()) { |
2063 | case Declaration::FIELD: { |
2064 | parent.childCount++; |
2065 | memberInfo = &arena.allocate<MemberInfo>( |
2066 | parent, codeOrder++, member, layout, false); |
2067 | allMembers.add(memberInfo); |
2068 | ordinal = member.getId().getOrdinal().getValue(); |
2069 | break; |
2070 | } |
2071 | |
2072 | case Declaration::UNION: { |
2073 | StructLayout::Union& unionLayout = arena.allocate<StructLayout::Union>(layout); |
2074 | |
2075 | uint independentSubCodeOrder = 0; |
2076 | uint* subCodeOrder = &independentSubCodeOrder; |
2077 | if (member.getName().getValue() == "" ) { |
2078 | memberInfo = &parent; |
2079 | subCodeOrder = &codeOrder; |
2080 | } else { |
2081 | parent.childCount++; |
2082 | memberInfo = &arena.allocate<MemberInfo>( |
2083 | parent, codeOrder++, member, |
2084 | newGroupNode(parent.node, member.getName().getValue()), |
2085 | false); |
2086 | allMembers.add(memberInfo); |
2087 | } |
2088 | memberInfo->unionScope = &unionLayout; |
2089 | traverseUnion(member, member.getNestedDecls(), *memberInfo, unionLayout, *subCodeOrder); |
2090 | if (member.getId().isOrdinal()) { |
2091 | ordinal = member.getId().getOrdinal().getValue(); |
2092 | } |
2093 | break; |
2094 | } |
2095 | |
2096 | case Declaration::GROUP: |
2097 | parent.childCount++; |
2098 | memberInfo = &arena.allocate<MemberInfo>( |
2099 | parent, codeOrder++, member, |
2100 | newGroupNode(parent.node, member.getName().getValue()), |
2101 | false); |
2102 | allMembers.add(memberInfo); |
2103 | |
2104 | // Members of the group are laid out just like they were members of the parent, so we |
2105 | // just pass along the parent layout. |
2106 | traverseGroup(member.getNestedDecls(), *memberInfo, layout); |
2107 | |
2108 | // No ordinal for groups. |
2109 | break; |
2110 | |
2111 | default: |
2112 | // Ignore others. |
2113 | break; |
2114 | } |
2115 | |
2116 | KJ_IF_MAYBE(o, ordinal) { |
2117 | membersByOrdinal.insert(std::make_pair(*o, memberInfo)); |
2118 | } |
2119 | } |
2120 | } |
2121 | |
2122 | void traverseParams(List<Declaration::Param>::Reader params, MemberInfo& parent, |
2123 | StructLayout::StructOrGroup& layout) { |
2124 | for (uint i: kj::indices(params)) { |
2125 | auto param = params[i]; |
2126 | parent.childCount++; |
2127 | MemberInfo* memberInfo = &arena.allocate<MemberInfo>(parent, i, param, layout, false); |
2128 | allMembers.add(memberInfo); |
2129 | membersByOrdinal.insert(std::make_pair(i, memberInfo)); |
2130 | } |
2131 | } |
2132 | |
2133 | NodeSourceInfoBuilderPair newGroupNode(schema::Node::Reader parent, kj::StringPtr name) { |
2134 | AuxNode aux { |
2135 | translator.orphanage.newOrphan<schema::Node>(), |
2136 | translator.orphanage.newOrphan<schema::Node::SourceInfo>() |
2137 | }; |
2138 | auto node = aux.node.get(); |
2139 | auto sourceInfo = aux.sourceInfo.get(); |
2140 | |
2141 | // We'll set the ID and scope ID later. |
2142 | node.setDisplayName(kj::str(parent.getDisplayName(), '.', name)); |
2143 | node.setDisplayNamePrefixLength(node.getDisplayName().size() - name.size()); |
2144 | node.setIsGeneric(parent.getIsGeneric()); |
2145 | node.initStruct().setIsGroup(true); |
2146 | |
2147 | // The remaining contents of node.struct will be filled in later. |
2148 | |
2149 | translator.groups.add(kj::mv(aux)); |
2150 | return { node, sourceInfo }; |
2151 | } |
2152 | |
2153 | void translateInternal(MemberInfo& root, schema::Node::Builder builder) { |
2154 | auto structBuilder = builder.initStruct(); |
2155 | |
2156 | // Go through each member in ordinal order, building each member schema. |
2157 | DuplicateOrdinalDetector dupDetector(errorReporter); |
2158 | for (auto& entry: membersByOrdinal) { |
2159 | MemberInfo& member = *entry.second; |
2160 | |
2161 | // Make sure the exceptions added relating to |
2162 | // https://github.com/sandstorm-io/capnproto/issues/344 identify the affected field. |
2163 | KJ_CONTEXT(member.name); |
2164 | |
2165 | if (member.declId.isOrdinal()) { |
2166 | dupDetector.check(member.declId.getOrdinal()); |
2167 | } |
2168 | |
2169 | schema::Field::Builder fieldBuilder = member.getSchema(); |
2170 | fieldBuilder.getOrdinal().setExplicit(entry.first); |
2171 | |
2172 | switch (member.declKind) { |
2173 | case Declaration::FIELD: { |
2174 | auto slot = fieldBuilder.initSlot(); |
2175 | auto typeBuilder = slot.initType(); |
2176 | if (translator.compileType(member.fieldType, typeBuilder, implicitMethodParams)) { |
2177 | if (member.hasDefaultValue) { |
2178 | if (member.isParam && |
2179 | member.fieldDefaultValue.isRelativeName() && |
2180 | member.fieldDefaultValue.getRelativeName().getValue() == "null" ) { |
2181 | // special case: parameter set null |
2182 | switch (typeBuilder.which()) { |
2183 | case schema::Type::TEXT: |
2184 | case schema::Type::DATA: |
2185 | case schema::Type::LIST: |
2186 | case schema::Type::STRUCT: |
2187 | case schema::Type::INTERFACE: |
2188 | case schema::Type::ANY_POINTER: |
2189 | break; |
2190 | default: |
2191 | errorReporter.addErrorOn(member.fieldDefaultValue.getRelativeName(), |
2192 | "Only pointer parameters can declare their default as 'null'." ); |
2193 | break; |
2194 | } |
2195 | translator.compileDefaultDefaultValue(typeBuilder, slot.initDefaultValue()); |
2196 | } else { |
2197 | translator.compileBootstrapValue(member.fieldDefaultValue, |
2198 | typeBuilder, slot.initDefaultValue()); |
2199 | } |
2200 | slot.setHadExplicitDefault(true); |
2201 | } else { |
2202 | translator.compileDefaultDefaultValue(typeBuilder, slot.initDefaultValue()); |
2203 | } |
2204 | } else { |
2205 | translator.compileDefaultDefaultValue(typeBuilder, slot.initDefaultValue()); |
2206 | } |
2207 | |
2208 | int lgSize = -1; |
2209 | switch (typeBuilder.which()) { |
2210 | case schema::Type::VOID: lgSize = -1; break; |
2211 | case schema::Type::BOOL: lgSize = 0; break; |
2212 | case schema::Type::INT8: lgSize = 3; break; |
2213 | case schema::Type::INT16: lgSize = 4; break; |
2214 | case schema::Type::INT32: lgSize = 5; break; |
2215 | case schema::Type::INT64: lgSize = 6; break; |
2216 | case schema::Type::UINT8: lgSize = 3; break; |
2217 | case schema::Type::UINT16: lgSize = 4; break; |
2218 | case schema::Type::UINT32: lgSize = 5; break; |
2219 | case schema::Type::UINT64: lgSize = 6; break; |
2220 | case schema::Type::FLOAT32: lgSize = 5; break; |
2221 | case schema::Type::FLOAT64: lgSize = 6; break; |
2222 | |
2223 | case schema::Type::TEXT: lgSize = -2; break; |
2224 | case schema::Type::DATA: lgSize = -2; break; |
2225 | case schema::Type::LIST: lgSize = -2; break; |
2226 | case schema::Type::ENUM: lgSize = 4; break; |
2227 | case schema::Type::STRUCT: lgSize = -2; break; |
2228 | case schema::Type::INTERFACE: lgSize = -2; break; |
2229 | case schema::Type::ANY_POINTER: lgSize = -2; break; |
2230 | } |
2231 | |
2232 | if (lgSize == -2) { |
2233 | // pointer |
2234 | slot.setOffset(member.fieldScope->addPointer()); |
2235 | } else if (lgSize == -1) { |
2236 | // void |
2237 | member.fieldScope->addVoid(); |
2238 | slot.setOffset(0); |
2239 | } else { |
2240 | slot.setOffset(member.fieldScope->addData(lgSize)); |
2241 | } |
2242 | break; |
2243 | } |
2244 | |
2245 | case Declaration::UNION: |
2246 | if (!member.unionScope->addDiscriminant()) { |
2247 | errorReporter.addErrorOn(member.declId.getOrdinal(), |
2248 | "Union ordinal, if specified, must be greater than no more than one of its " |
2249 | "member ordinals (i.e. there can only be one field retroactively unionized)." ); |
2250 | } |
2251 | break; |
2252 | |
2253 | case Declaration::GROUP: |
2254 | KJ_FAIL_ASSERT("Groups don't have ordinals." ); |
2255 | break; |
2256 | |
2257 | default: |
2258 | KJ_FAIL_ASSERT("Unexpected member type." ); |
2259 | break; |
2260 | } |
2261 | } |
2262 | |
2263 | // OK, we should have built all the members. Now go through and make sure the discriminant |
2264 | // offsets have been copied over to the schemas and annotations have been applied. |
2265 | root.finishGroup(); |
2266 | for (auto member: allMembers) { |
2267 | kj::StringPtr targetsFlagName; |
2268 | if (member->isParam) { |
2269 | targetsFlagName = "targetsParam" ; |
2270 | } else { |
2271 | switch (member->declKind) { |
2272 | case Declaration::FIELD: |
2273 | targetsFlagName = "targetsField" ; |
2274 | break; |
2275 | |
2276 | case Declaration::UNION: |
2277 | member->finishGroup(); |
2278 | targetsFlagName = "targetsUnion" ; |
2279 | break; |
2280 | |
2281 | case Declaration::GROUP: |
2282 | member->finishGroup(); |
2283 | targetsFlagName = "targetsGroup" ; |
2284 | break; |
2285 | |
2286 | default: |
2287 | KJ_FAIL_ASSERT("Unexpected member type." ); |
2288 | break; |
2289 | } |
2290 | } |
2291 | |
2292 | member->getSchema().adoptAnnotations(translator.compileAnnotationApplications( |
2293 | member->declAnnotations, targetsFlagName)); |
2294 | } |
2295 | |
2296 | // And fill in the sizes. |
2297 | structBuilder.setDataWordCount(layout.getTop().dataWordCount); |
2298 | structBuilder.setPointerCount(layout.getTop().pointerCount); |
2299 | structBuilder.setPreferredListEncoding(schema::ElementSize::INLINE_COMPOSITE); |
2300 | |
2301 | for (auto& group: translator.groups) { |
2302 | auto groupBuilder = group.node.get().getStruct(); |
2303 | groupBuilder.setDataWordCount(structBuilder.getDataWordCount()); |
2304 | groupBuilder.setPointerCount(structBuilder.getPointerCount()); |
2305 | groupBuilder.setPreferredListEncoding(structBuilder.getPreferredListEncoding()); |
2306 | } |
2307 | } |
2308 | }; |
2309 | |
2310 | void NodeTranslator::compileStruct(Void decl, List<Declaration>::Reader members, |
2311 | schema::Node::Builder builder) { |
2312 | StructTranslator(*this, noImplicitParams()).translate(decl, members, builder, sourceInfo.get()); |
2313 | } |
2314 | |
2315 | // ------------------------------------------------------------------- |
2316 | |
2317 | static kj::String expressionString(Expression::Reader name); |
2318 | |
2319 | void NodeTranslator::compileInterface(Declaration::Interface::Reader decl, |
2320 | List<Declaration>::Reader members, |
2321 | schema::Node::Builder builder) { |
2322 | auto interfaceBuilder = builder.initInterface(); |
2323 | |
2324 | auto superclassesDecl = decl.getSuperclasses(); |
2325 | auto superclassesBuilder = interfaceBuilder.initSuperclasses(superclassesDecl.size()); |
2326 | for (uint i: kj::indices(superclassesDecl)) { |
2327 | auto superclass = superclassesDecl[i]; |
2328 | |
2329 | KJ_IF_MAYBE(decl, compileDeclExpression(superclass, noImplicitParams())) { |
2330 | KJ_IF_MAYBE(kind, decl->getKind()) { |
2331 | if (*kind == Declaration::INTERFACE) { |
2332 | auto s = superclassesBuilder[i]; |
2333 | s.setId(decl->getIdAndFillBrand([&]() { return s.initBrand(); })); |
2334 | } else { |
2335 | decl->addError(errorReporter, kj::str( |
2336 | "'" , decl->toString(), "' is not an interface." )); |
2337 | } |
2338 | } else { |
2339 | // A variable? |
2340 | decl->addError(errorReporter, kj::str( |
2341 | "'" , decl->toString(), "' is an unbound generic parameter. Currently we don't support " |
2342 | "extending these." )); |
2343 | } |
2344 | } |
2345 | } |
2346 | |
2347 | // maps ordinal -> (code order, declaration) |
2348 | std::multimap<uint, std::pair<uint, Declaration::Reader>> methods; |
2349 | |
2350 | uint codeOrder = 0; |
2351 | for (auto member: members) { |
2352 | if (member.isMethod()) { |
2353 | methods.insert( |
2354 | std::make_pair(member.getId().getOrdinal().getValue(), |
2355 | std::make_pair(codeOrder++, member))); |
2356 | } |
2357 | } |
2358 | |
2359 | auto list = interfaceBuilder.initMethods(methods.size()); |
2360 | auto sourceInfoList = sourceInfo.get().initMembers(methods.size()); |
2361 | uint i = 0; |
2362 | DuplicateOrdinalDetector dupDetector(errorReporter); |
2363 | |
2364 | for (auto& entry: methods) { |
2365 | uint codeOrder = entry.second.first; |
2366 | Declaration::Reader methodDecl = entry.second.second; |
2367 | auto methodReader = methodDecl.getMethod(); |
2368 | |
2369 | auto ordinalDecl = methodDecl.getId().getOrdinal(); |
2370 | dupDetector.check(ordinalDecl); |
2371 | uint16_t ordinal = ordinalDecl.getValue(); |
2372 | |
2373 | if (methodDecl.hasDocComment()) { |
2374 | sourceInfoList[i].setDocComment(methodDecl.getDocComment()); |
2375 | } |
2376 | |
2377 | auto methodBuilder = list[i++]; |
2378 | methodBuilder.setName(methodDecl.getName().getValue()); |
2379 | methodBuilder.setCodeOrder(codeOrder); |
2380 | |
2381 | auto implicits = methodDecl.getParameters(); |
2382 | auto implicitsBuilder = methodBuilder.initImplicitParameters(implicits.size()); |
2383 | for (auto i: kj::indices(implicits)) { |
2384 | implicitsBuilder[i].setName(implicits[i].getName()); |
2385 | } |
2386 | |
2387 | methodBuilder.setParamStructType(compileParamList( |
2388 | methodDecl.getName().getValue(), ordinal, false, |
2389 | methodReader.getParams(), implicits, |
2390 | [&]() { return methodBuilder.initParamBrand(); })); |
2391 | |
2392 | auto results = methodReader.getResults(); |
2393 | Declaration::ParamList::Reader resultList; |
2394 | if (results.isExplicit()) { |
2395 | resultList = results.getExplicit(); |
2396 | } else { |
2397 | // We just stick with `resultList` uninitialized, which is equivalent to the default |
2398 | // instance. This works because `namedList` is the default kind of ParamList, and it will |
2399 | // default to an empty list. |
2400 | } |
2401 | methodBuilder.setResultStructType(compileParamList( |
2402 | methodDecl.getName().getValue(), ordinal, true, |
2403 | resultList, implicits, |
2404 | [&]() { return methodBuilder.initResultBrand(); })); |
2405 | |
2406 | methodBuilder.adoptAnnotations(compileAnnotationApplications( |
2407 | methodDecl.getAnnotations(), "targetsMethod" )); |
2408 | } |
2409 | } |
2410 | |
2411 | template <typename InitBrandFunc> |
2412 | uint64_t NodeTranslator::compileParamList( |
2413 | kj::StringPtr methodName, uint16_t ordinal, bool isResults, |
2414 | Declaration::ParamList::Reader paramList, |
2415 | typename List<Declaration::BrandParameter>::Reader implicitParams, |
2416 | InitBrandFunc&& initBrand) { |
2417 | switch (paramList.which()) { |
2418 | case Declaration::ParamList::NAMED_LIST: { |
2419 | auto newStruct = orphanage.newOrphan<schema::Node>(); |
2420 | auto newSourceInfo = orphanage.newOrphan<schema::Node::SourceInfo>(); |
2421 | auto builder = newStruct.get(); |
2422 | auto parent = wipNode.getReader(); |
2423 | |
2424 | kj::String typeName = kj::str(methodName, isResults ? "$Results" : "$Params" ); |
2425 | |
2426 | builder.setId(generateMethodParamsId(parent.getId(), ordinal, isResults)); |
2427 | builder.setDisplayName(kj::str(parent.getDisplayName(), '.', typeName)); |
2428 | builder.setDisplayNamePrefixLength(builder.getDisplayName().size() - typeName.size()); |
2429 | builder.setIsGeneric(parent.getIsGeneric() || implicitParams.size() > 0); |
2430 | builder.setScopeId(0); // detached struct type |
2431 | |
2432 | builder.initStruct(); |
2433 | |
2434 | // Note that the struct we create here has a brand parameter list mirrioring the method's |
2435 | // implicit parameter list. Of course, fields inside the struct using the method's implicit |
2436 | // params as types actually need to refer to them as regular params, so we create an |
2437 | // ImplicitParams with a scopeId here. |
2438 | StructTranslator(*this, ImplicitParams { builder.getId(), implicitParams }) |
2439 | .translate(paramList.getNamedList(), builder, newSourceInfo.get()); |
2440 | uint64_t id = builder.getId(); |
2441 | paramStructs.add(AuxNode { kj::mv(newStruct), kj::mv(newSourceInfo) }); |
2442 | |
2443 | auto brand = localBrand->push(builder.getId(), implicitParams.size()); |
2444 | |
2445 | if (implicitParams.size() > 0) { |
2446 | auto implicitDecls = kj::heapArrayBuilder<BrandedDecl>(implicitParams.size()); |
2447 | auto implicitBuilder = builder.initParameters(implicitParams.size()); |
2448 | |
2449 | for (auto i: kj::indices(implicitParams)) { |
2450 | auto param = implicitParams[i]; |
2451 | implicitDecls.add(BrandedDecl::implicitMethodParam(i)); |
2452 | implicitBuilder[i].setName(param.getName()); |
2453 | } |
2454 | |
2455 | brand->setParams(implicitDecls.finish(), Declaration::STRUCT, Expression::Reader()); |
2456 | } |
2457 | |
2458 | brand->compile(initBrand); |
2459 | return id; |
2460 | } |
2461 | case Declaration::ParamList::TYPE: |
2462 | KJ_IF_MAYBE(target, compileDeclExpression( |
2463 | paramList.getType(), ImplicitParams { 0, implicitParams })) { |
2464 | KJ_IF_MAYBE(kind, target->getKind()) { |
2465 | if (*kind == Declaration::STRUCT) { |
2466 | return target->getIdAndFillBrand(kj::fwd<InitBrandFunc>(initBrand)); |
2467 | } else { |
2468 | errorReporter.addErrorOn( |
2469 | paramList.getType(), |
2470 | kj::str("'" , expressionString(paramList.getType()), "' is not a struct type." )); |
2471 | } |
2472 | } else { |
2473 | // A variable? |
2474 | target->addError(errorReporter, |
2475 | "Cannot use generic parameter as whole input or output of a method. Instead, " |
2476 | "use a parameter/result list containing a field with this type." ); |
2477 | return 0; |
2478 | } |
2479 | } |
2480 | return 0; |
2481 | } |
2482 | KJ_UNREACHABLE; |
2483 | } |
2484 | |
2485 | // ------------------------------------------------------------------- |
2486 | |
2487 | static const char HEXDIGITS[] = "0123456789abcdef" ; |
2488 | |
2489 | static kj::StringTree stringLiteral(kj::StringPtr chars) { |
2490 | return kj::strTree('"', kj::encodeCEscape(chars), '"'); |
2491 | } |
2492 | |
2493 | static kj::StringTree binaryLiteral(Data::Reader data) { |
2494 | kj::Vector<char> escaped(data.size() * 3); |
2495 | |
2496 | for (byte b: data) { |
2497 | escaped.add(HEXDIGITS[b % 16]); |
2498 | escaped.add(HEXDIGITS[b / 16]); |
2499 | escaped.add(' '); |
2500 | } |
2501 | |
2502 | escaped.removeLast(); |
2503 | return kj::strTree("0x\"" , escaped, '"'); |
2504 | } |
2505 | |
2506 | static kj::StringTree expressionStringTree(Expression::Reader exp); |
2507 | |
2508 | static kj::StringTree tupleLiteral(List<Expression::Param>::Reader params) { |
2509 | auto parts = kj::heapArrayBuilder<kj::StringTree>(params.size()); |
2510 | for (auto param: params) { |
2511 | auto part = expressionStringTree(param.getValue()); |
2512 | if (param.isNamed()) { |
2513 | part = kj::strTree(param.getNamed().getValue(), " = " , kj::mv(part)); |
2514 | } |
2515 | parts.add(kj::mv(part)); |
2516 | } |
2517 | return kj::strTree("( " , kj::StringTree(parts.finish(), ", " ), " )" ); |
2518 | } |
2519 | |
2520 | static kj::StringTree expressionStringTree(Expression::Reader exp) { |
2521 | switch (exp.which()) { |
2522 | case Expression::UNKNOWN: |
2523 | return kj::strTree("<parse error>" ); |
2524 | case Expression::POSITIVE_INT: |
2525 | return kj::strTree(exp.getPositiveInt()); |
2526 | case Expression::NEGATIVE_INT: |
2527 | return kj::strTree('-', exp.getNegativeInt()); |
2528 | case Expression::FLOAT: |
2529 | return kj::strTree(exp.getFloat()); |
2530 | case Expression::STRING: |
2531 | return stringLiteral(exp.getString()); |
2532 | case Expression::BINARY: |
2533 | return binaryLiteral(exp.getBinary()); |
2534 | case Expression::RELATIVE_NAME: |
2535 | return kj::strTree(exp.getRelativeName().getValue()); |
2536 | case Expression::ABSOLUTE_NAME: |
2537 | return kj::strTree('.', exp.getAbsoluteName().getValue()); |
2538 | case Expression::IMPORT: |
2539 | return kj::strTree("import " , stringLiteral(exp.getImport().getValue())); |
2540 | case Expression::EMBED: |
2541 | return kj::strTree("embed " , stringLiteral(exp.getEmbed().getValue())); |
2542 | |
2543 | case Expression::LIST: { |
2544 | auto list = exp.getList(); |
2545 | auto parts = kj::heapArrayBuilder<kj::StringTree>(list.size()); |
2546 | for (auto element: list) { |
2547 | parts.add(expressionStringTree(element)); |
2548 | } |
2549 | return kj::strTree("[ " , kj::StringTree(parts.finish(), ", " ), " ]" ); |
2550 | } |
2551 | |
2552 | case Expression::TUPLE: |
2553 | return tupleLiteral(exp.getTuple()); |
2554 | |
2555 | case Expression::APPLICATION: { |
2556 | auto app = exp.getApplication(); |
2557 | return kj::strTree(expressionStringTree(app.getFunction()), |
2558 | '(', tupleLiteral(app.getParams()), ')'); |
2559 | } |
2560 | |
2561 | case Expression::MEMBER: { |
2562 | auto member = exp.getMember(); |
2563 | return kj::strTree(expressionStringTree(member.getParent()), '.', |
2564 | member.getName().getValue()); |
2565 | } |
2566 | } |
2567 | |
2568 | KJ_UNREACHABLE; |
2569 | } |
2570 | |
2571 | static kj::String expressionString(Expression::Reader name) { |
2572 | return expressionStringTree(name).flatten(); |
2573 | } |
2574 | |
2575 | // ------------------------------------------------------------------- |
2576 | |
2577 | kj::Maybe<NodeTranslator::BrandedDecl> |
2578 | NodeTranslator::compileDeclExpression( |
2579 | Expression::Reader source, ImplicitParams implicitMethodParams) { |
2580 | return localBrand->compileDeclExpression(source, resolver, implicitMethodParams); |
2581 | } |
2582 | |
2583 | /* static */ kj::Maybe<NodeTranslator::Resolver::ResolveResult> NodeTranslator::compileDecl( |
2584 | uint64_t scopeId, uint scopeParameterCount, Resolver& resolver, ErrorReporter& errorReporter, |
2585 | Expression::Reader expression, schema::Brand::Builder brandBuilder) { |
2586 | auto scope = kj::refcounted<BrandScope>(errorReporter, scopeId, scopeParameterCount, resolver); |
2587 | KJ_IF_MAYBE(decl, scope->compileDeclExpression(expression, resolver, noImplicitParams())) { |
2588 | return decl->asResolveResult(scope->getScopeId(), brandBuilder); |
2589 | } else { |
2590 | return nullptr; |
2591 | } |
2592 | } |
2593 | |
2594 | bool NodeTranslator::compileType(Expression::Reader source, schema::Type::Builder target, |
2595 | ImplicitParams implicitMethodParams) { |
2596 | KJ_IF_MAYBE(decl, compileDeclExpression(source, implicitMethodParams)) { |
2597 | return decl->compileAsType(errorReporter, target); |
2598 | } else { |
2599 | return false; |
2600 | } |
2601 | } |
2602 | |
2603 | // ------------------------------------------------------------------- |
2604 | |
2605 | void NodeTranslator::compileDefaultDefaultValue( |
2606 | schema::Type::Reader type, schema::Value::Builder target) { |
2607 | switch (type.which()) { |
2608 | case schema::Type::VOID: target.setVoid(); break; |
2609 | case schema::Type::BOOL: target.setBool(false); break; |
2610 | case schema::Type::INT8: target.setInt8(0); break; |
2611 | case schema::Type::INT16: target.setInt16(0); break; |
2612 | case schema::Type::INT32: target.setInt32(0); break; |
2613 | case schema::Type::INT64: target.setInt64(0); break; |
2614 | case schema::Type::UINT8: target.setUint8(0); break; |
2615 | case schema::Type::UINT16: target.setUint16(0); break; |
2616 | case schema::Type::UINT32: target.setUint32(0); break; |
2617 | case schema::Type::UINT64: target.setUint64(0); break; |
2618 | case schema::Type::FLOAT32: target.setFloat32(0); break; |
2619 | case schema::Type::FLOAT64: target.setFloat64(0); break; |
2620 | case schema::Type::ENUM: target.setEnum(0); break; |
2621 | case schema::Type::INTERFACE: target.setInterface(); break; |
2622 | |
2623 | // Bit of a hack: For Text/Data, we adopt a null orphan, which sets the field to null. |
2624 | // TODO(cleanup): Create a cleaner way to do this. |
2625 | case schema::Type::TEXT: target.adoptText(Orphan<Text>()); break; |
2626 | case schema::Type::DATA: target.adoptData(Orphan<Data>()); break; |
2627 | case schema::Type::STRUCT: target.initStruct(); break; |
2628 | case schema::Type::LIST: target.initList(); break; |
2629 | case schema::Type::ANY_POINTER: target.initAnyPointer(); break; |
2630 | } |
2631 | } |
2632 | |
2633 | void NodeTranslator::compileBootstrapValue( |
2634 | Expression::Reader source, schema::Type::Reader type, schema::Value::Builder target, |
2635 | Schema typeScope) { |
2636 | // Start by filling in a default default value so that if for whatever reason we don't end up |
2637 | // initializing the value, this won't cause schema validation to fail. |
2638 | compileDefaultDefaultValue(type, target); |
2639 | |
2640 | switch (type.which()) { |
2641 | case schema::Type::LIST: |
2642 | case schema::Type::STRUCT: |
2643 | case schema::Type::INTERFACE: |
2644 | case schema::Type::ANY_POINTER: |
2645 | unfinishedValues.add(UnfinishedValue { source, type, typeScope, target }); |
2646 | break; |
2647 | |
2648 | default: |
2649 | // Primitive value. |
2650 | compileValue(source, type, typeScope, target, true); |
2651 | break; |
2652 | } |
2653 | } |
2654 | |
2655 | void NodeTranslator::compileValue(Expression::Reader source, schema::Type::Reader type, |
2656 | Schema typeScope, schema::Value::Builder target, |
2657 | bool isBootstrap) { |
2658 | class ResolverGlue: public ValueTranslator::Resolver { |
2659 | public: |
2660 | inline ResolverGlue(NodeTranslator& translator, bool isBootstrap) |
2661 | : translator(translator), isBootstrap(isBootstrap) {} |
2662 | |
2663 | kj::Maybe<DynamicValue::Reader> resolveConstant(Expression::Reader name) override { |
2664 | return translator.readConstant(name, isBootstrap); |
2665 | } |
2666 | |
2667 | kj::Maybe<kj::Array<const byte>> readEmbed(LocatedText::Reader filename) override { |
2668 | return translator.readEmbed(filename); |
2669 | } |
2670 | |
2671 | private: |
2672 | NodeTranslator& translator; |
2673 | bool isBootstrap; |
2674 | }; |
2675 | |
2676 | ResolverGlue glue(*this, isBootstrap); |
2677 | ValueTranslator valueTranslator(glue, errorReporter, orphanage); |
2678 | |
2679 | KJ_IF_MAYBE(typeSchema, resolver.resolveBootstrapType(type, typeScope)) { |
2680 | kj::StringPtr fieldName = Schema::from<schema::Type>() |
2681 | .getUnionFields()[static_cast<uint>(typeSchema->which())].getProto().getName(); |
2682 | |
2683 | KJ_IF_MAYBE(value, valueTranslator.compileValue(source, *typeSchema)) { |
2684 | if (typeSchema->isEnum()) { |
2685 | target.setEnum(value->getReader().as<DynamicEnum>().getRaw()); |
2686 | } else { |
2687 | toDynamic(target).adopt(fieldName, kj::mv(*value)); |
2688 | } |
2689 | } |
2690 | } |
2691 | } |
2692 | |
2693 | kj::Maybe<Orphan<DynamicValue>> ValueTranslator::compileValue(Expression::Reader src, Type type) { |
2694 | Orphan<DynamicValue> result = compileValueInner(src, type); |
2695 | |
2696 | switch (result.getType()) { |
2697 | case DynamicValue::UNKNOWN: |
2698 | // Error already reported. |
2699 | return nullptr; |
2700 | |
2701 | case DynamicValue::VOID: |
2702 | if (type.isVoid()) { |
2703 | return kj::mv(result); |
2704 | } |
2705 | break; |
2706 | |
2707 | case DynamicValue::BOOL: |
2708 | if (type.isBool()) { |
2709 | return kj::mv(result); |
2710 | } |
2711 | break; |
2712 | |
2713 | case DynamicValue::INT: { |
2714 | int64_t value = result.getReader().as<int64_t>(); |
2715 | if (value < 0) { |
2716 | int64_t minValue = 1; |
2717 | switch (type.which()) { |
2718 | case schema::Type::INT8: minValue = (int8_t)kj::minValue; break; |
2719 | case schema::Type::INT16: minValue = (int16_t)kj::minValue; break; |
2720 | case schema::Type::INT32: minValue = (int32_t)kj::minValue; break; |
2721 | case schema::Type::INT64: minValue = (int64_t)kj::minValue; break; |
2722 | case schema::Type::UINT8: minValue = (uint8_t)kj::minValue; break; |
2723 | case schema::Type::UINT16: minValue = (uint16_t)kj::minValue; break; |
2724 | case schema::Type::UINT32: minValue = (uint32_t)kj::minValue; break; |
2725 | case schema::Type::UINT64: minValue = (uint64_t)kj::minValue; break; |
2726 | |
2727 | case schema::Type::FLOAT32: |
2728 | case schema::Type::FLOAT64: |
2729 | // Any integer is acceptable. |
2730 | minValue = (int64_t)kj::minValue; |
2731 | break; |
2732 | |
2733 | default: break; |
2734 | } |
2735 | if (minValue == 1) break; |
2736 | |
2737 | if (value < minValue) { |
2738 | errorReporter.addErrorOn(src, "Integer value out of range." ); |
2739 | result = minValue; |
2740 | } |
2741 | return kj::mv(result); |
2742 | } |
2743 | |
2744 | } // fallthrough -- value is positive, so we can just go on to the uint case below. |
2745 | |
2746 | case DynamicValue::UINT: { |
2747 | uint64_t maxValue = 0; |
2748 | switch (type.which()) { |
2749 | case schema::Type::INT8: maxValue = (int8_t)kj::maxValue; break; |
2750 | case schema::Type::INT16: maxValue = (int16_t)kj::maxValue; break; |
2751 | case schema::Type::INT32: maxValue = (int32_t)kj::maxValue; break; |
2752 | case schema::Type::INT64: maxValue = (int64_t)kj::maxValue; break; |
2753 | case schema::Type::UINT8: maxValue = (uint8_t)kj::maxValue; break; |
2754 | case schema::Type::UINT16: maxValue = (uint16_t)kj::maxValue; break; |
2755 | case schema::Type::UINT32: maxValue = (uint32_t)kj::maxValue; break; |
2756 | case schema::Type::UINT64: maxValue = (uint64_t)kj::maxValue; break; |
2757 | |
2758 | case schema::Type::FLOAT32: |
2759 | case schema::Type::FLOAT64: |
2760 | // Any integer is acceptable. |
2761 | maxValue = (uint64_t)kj::maxValue; |
2762 | break; |
2763 | |
2764 | default: break; |
2765 | } |
2766 | if (maxValue == 0) break; |
2767 | |
2768 | if (result.getReader().as<uint64_t>() > maxValue) { |
2769 | errorReporter.addErrorOn(src, "Integer value out of range." ); |
2770 | result = maxValue; |
2771 | } |
2772 | return kj::mv(result); |
2773 | } |
2774 | |
2775 | case DynamicValue::FLOAT: |
2776 | if (type.isFloat32() || type.isFloat64()) { |
2777 | return kj::mv(result); |
2778 | } |
2779 | break; |
2780 | |
2781 | case DynamicValue::TEXT: |
2782 | if (type.isText()) { |
2783 | return kj::mv(result); |
2784 | } |
2785 | break; |
2786 | |
2787 | case DynamicValue::DATA: |
2788 | if (type.isData()) { |
2789 | return kj::mv(result); |
2790 | } |
2791 | break; |
2792 | |
2793 | case DynamicValue::LIST: |
2794 | if (type.isList()) { |
2795 | if (result.getReader().as<DynamicList>().getSchema() == type.asList()) { |
2796 | return kj::mv(result); |
2797 | } |
2798 | } else if (type.isAnyPointer()) { |
2799 | switch (type.whichAnyPointerKind()) { |
2800 | case schema::Type::AnyPointer::Unconstrained::ANY_KIND: |
2801 | case schema::Type::AnyPointer::Unconstrained::LIST: |
2802 | return kj::mv(result); |
2803 | case schema::Type::AnyPointer::Unconstrained::STRUCT: |
2804 | case schema::Type::AnyPointer::Unconstrained::CAPABILITY: |
2805 | break; |
2806 | } |
2807 | } |
2808 | break; |
2809 | |
2810 | case DynamicValue::ENUM: |
2811 | if (type.isEnum()) { |
2812 | if (result.getReader().as<DynamicEnum>().getSchema() == type.asEnum()) { |
2813 | return kj::mv(result); |
2814 | } |
2815 | } |
2816 | break; |
2817 | |
2818 | case DynamicValue::STRUCT: |
2819 | if (type.isStruct()) { |
2820 | if (result.getReader().as<DynamicStruct>().getSchema() == type.asStruct()) { |
2821 | return kj::mv(result); |
2822 | } |
2823 | } else if (type.isAnyPointer()) { |
2824 | switch (type.whichAnyPointerKind()) { |
2825 | case schema::Type::AnyPointer::Unconstrained::ANY_KIND: |
2826 | case schema::Type::AnyPointer::Unconstrained::STRUCT: |
2827 | return kj::mv(result); |
2828 | case schema::Type::AnyPointer::Unconstrained::LIST: |
2829 | case schema::Type::AnyPointer::Unconstrained::CAPABILITY: |
2830 | break; |
2831 | } |
2832 | } |
2833 | break; |
2834 | |
2835 | case DynamicValue::CAPABILITY: |
2836 | KJ_FAIL_ASSERT("Interfaces can't have literal values." ); |
2837 | |
2838 | case DynamicValue::ANY_POINTER: |
2839 | KJ_FAIL_ASSERT("AnyPointers can't have literal values." ); |
2840 | } |
2841 | |
2842 | errorReporter.addErrorOn(src, kj::str("Type mismatch; expected " , makeTypeName(type), "." )); |
2843 | return nullptr; |
2844 | } |
2845 | |
2846 | Orphan<DynamicValue> ValueTranslator::compileValueInner(Expression::Reader src, Type type) { |
2847 | switch (src.which()) { |
2848 | case Expression::RELATIVE_NAME: { |
2849 | auto name = src.getRelativeName(); |
2850 | |
2851 | // The name is just a bare identifier. It may be a literal value or an enumerant. |
2852 | kj::StringPtr id = name.getValue(); |
2853 | |
2854 | if (type.isEnum()) { |
2855 | KJ_IF_MAYBE(enumerant, type.asEnum().findEnumerantByName(id)) { |
2856 | return DynamicEnum(*enumerant); |
2857 | } |
2858 | } else { |
2859 | // Interpret known constant values. |
2860 | if (id == "void" ) { |
2861 | return VOID; |
2862 | } else if (id == "true" ) { |
2863 | return true; |
2864 | } else if (id == "false" ) { |
2865 | return false; |
2866 | } else if (id == "nan" ) { |
2867 | return kj::nan(); |
2868 | } else if (id == "inf" ) { |
2869 | return kj::inf(); |
2870 | } |
2871 | } |
2872 | |
2873 | // Apparently not a literal. Try resolving it. |
2874 | KJ_IF_MAYBE(constValue, resolver.resolveConstant(src)) { |
2875 | return orphanage.newOrphanCopy(*constValue); |
2876 | } else { |
2877 | return nullptr; |
2878 | } |
2879 | } |
2880 | |
2881 | case Expression::ABSOLUTE_NAME: |
2882 | case Expression::IMPORT: |
2883 | case Expression::APPLICATION: |
2884 | case Expression::MEMBER: |
2885 | KJ_IF_MAYBE(constValue, resolver.resolveConstant(src)) { |
2886 | return orphanage.newOrphanCopy(*constValue); |
2887 | } else { |
2888 | return nullptr; |
2889 | } |
2890 | |
2891 | case Expression::EMBED: |
2892 | KJ_IF_MAYBE(data, resolver.readEmbed(src.getEmbed())) { |
2893 | switch (type.which()) { |
2894 | case schema::Type::TEXT: { |
2895 | // Sadly, we need to make a copy to add the NUL terminator. |
2896 | auto text = orphanage.newOrphan<Text>(data->size()); |
2897 | memcpy(text.get().begin(), data->begin(), data->size()); |
2898 | return kj::mv(text); |
2899 | } |
2900 | case schema::Type::DATA: |
2901 | // TODO(perf): It would arguably be neat to use orphanage.referenceExternalData(), |
2902 | // since typically the data is mmap()ed and this would avoid forcing a large file |
2903 | // to become memory-resident. However, we'd have to figure out who should own the |
2904 | // Array<byte>. Also, we'd have to deal with the possibility of misaligned data -- |
2905 | // though arguably in that case we know it's not mmap()ed so whatever. One more |
2906 | // thing: it would be neat to be able to reference text blobs this way too, if only |
2907 | // we could rely on the assumption that as long as the data doesn't end on a page |
2908 | // boundary, it will be zero-padded, thus giving us our NUL terminator (4095/4096 of |
2909 | // the time), but this seems to require documenting constraints on the underlying |
2910 | // file-reading interfaces. Hm. |
2911 | return orphanage.newOrphanCopy(Data::Reader(*data)); |
2912 | case schema::Type::STRUCT: { |
2913 | // We will almost certainly |
2914 | if (data->size() % sizeof(word) != 0) { |
2915 | errorReporter.addErrorOn(src, |
2916 | "Embedded file is not a valid Cap'n Proto message." ); |
2917 | return nullptr; |
2918 | } |
2919 | kj::Array<word> copy; |
2920 | kj::ArrayPtr<const word> words; |
2921 | if (reinterpret_cast<uintptr_t>(data->begin()) % sizeof(void*) == 0) { |
2922 | // Hooray, data is aligned. |
2923 | words = kj::ArrayPtr<const word>( |
2924 | reinterpret_cast<const word*>(data->begin()), |
2925 | data->size() / sizeof(word)); |
2926 | } else { |
2927 | // Ugh, data not aligned. Make a copy. |
2928 | copy = kj::heapArray<word>(data->size() / sizeof(word)); |
2929 | memcpy(copy.begin(), data->begin(), data->size()); |
2930 | words = copy; |
2931 | } |
2932 | ReaderOptions options; |
2933 | options.traversalLimitInWords = kj::maxValue; |
2934 | options.nestingLimit = kj::maxValue; |
2935 | FlatArrayMessageReader reader(words, options); |
2936 | return orphanage.newOrphanCopy(reader.getRoot<DynamicStruct>(type.asStruct())); |
2937 | } |
2938 | default: |
2939 | errorReporter.addErrorOn(src, |
2940 | "Embeds can only be used when Text, Data, or a struct is expected." ); |
2941 | return nullptr; |
2942 | } |
2943 | } else { |
2944 | return nullptr; |
2945 | } |
2946 | |
2947 | case Expression::POSITIVE_INT: |
2948 | return src.getPositiveInt(); |
2949 | |
2950 | case Expression::NEGATIVE_INT: { |
2951 | uint64_t nValue = src.getNegativeInt(); |
2952 | if (nValue > ((uint64_t)kj::maxValue >> 1) + 1) { |
2953 | errorReporter.addErrorOn(src, "Integer is too big to be negative." ); |
2954 | return nullptr; |
2955 | } else { |
2956 | return kj::implicitCast<int64_t>(-nValue); |
2957 | } |
2958 | } |
2959 | |
2960 | case Expression::FLOAT: |
2961 | return src.getFloat(); |
2962 | break; |
2963 | |
2964 | case Expression::STRING: |
2965 | if (type.isData()) { |
2966 | Text::Reader text = src.getString(); |
2967 | return orphanage.newOrphanCopy(Data::Reader(text.asBytes())); |
2968 | } else { |
2969 | return orphanage.newOrphanCopy(src.getString()); |
2970 | } |
2971 | break; |
2972 | |
2973 | case Expression::BINARY: |
2974 | if (!type.isData()) { |
2975 | errorReporter.addErrorOn(src, kj::str("Type mismatch; expected " , makeTypeName(type), "." )); |
2976 | return nullptr; |
2977 | } |
2978 | return orphanage.newOrphanCopy(src.getBinary()); |
2979 | |
2980 | case Expression::LIST: { |
2981 | if (!type.isList()) { |
2982 | errorReporter.addErrorOn(src, kj::str("Type mismatch; expected " , makeTypeName(type), "." )); |
2983 | return nullptr; |
2984 | } |
2985 | auto listSchema = type.asList(); |
2986 | Type elementType = listSchema.getElementType(); |
2987 | auto srcList = src.getList(); |
2988 | Orphan<DynamicList> result = orphanage.newOrphan(listSchema, srcList.size()); |
2989 | auto dstList = result.get(); |
2990 | for (uint i = 0; i < srcList.size(); i++) { |
2991 | KJ_IF_MAYBE(value, compileValue(srcList[i], elementType)) { |
2992 | dstList.adopt(i, kj::mv(*value)); |
2993 | } |
2994 | } |
2995 | return kj::mv(result); |
2996 | } |
2997 | |
2998 | case Expression::TUPLE: { |
2999 | if (!type.isStruct()) { |
3000 | errorReporter.addErrorOn(src, kj::str("Type mismatch; expected " , makeTypeName(type), "." )); |
3001 | return nullptr; |
3002 | } |
3003 | auto structSchema = type.asStruct(); |
3004 | Orphan<DynamicStruct> result = orphanage.newOrphan(structSchema); |
3005 | fillStructValue(result.get(), src.getTuple()); |
3006 | return kj::mv(result); |
3007 | } |
3008 | |
3009 | case Expression::UNKNOWN: |
3010 | // Ignore earlier error. |
3011 | return nullptr; |
3012 | } |
3013 | |
3014 | KJ_UNREACHABLE; |
3015 | } |
3016 | |
3017 | void ValueTranslator::fillStructValue(DynamicStruct::Builder builder, |
3018 | List<Expression::Param>::Reader assignments) { |
3019 | for (auto assignment: assignments) { |
3020 | if (assignment.isNamed()) { |
3021 | auto fieldName = assignment.getNamed(); |
3022 | KJ_IF_MAYBE(field, builder.getSchema().findFieldByName(fieldName.getValue())) { |
3023 | auto fieldProto = field->getProto(); |
3024 | auto value = assignment.getValue(); |
3025 | |
3026 | switch (fieldProto.which()) { |
3027 | case schema::Field::SLOT: |
3028 | KJ_IF_MAYBE(compiledValue, compileValue(value, field->getType())) { |
3029 | builder.adopt(*field, kj::mv(*compiledValue)); |
3030 | } |
3031 | break; |
3032 | |
3033 | case schema::Field::GROUP: |
3034 | if (value.isTuple()) { |
3035 | fillStructValue(builder.init(*field).as<DynamicStruct>(), value.getTuple()); |
3036 | } else { |
3037 | errorReporter.addErrorOn(value, "Type mismatch; expected group." ); |
3038 | } |
3039 | break; |
3040 | } |
3041 | } else { |
3042 | errorReporter.addErrorOn(fieldName, kj::str( |
3043 | "Struct has no field named '" , fieldName.getValue(), "'." )); |
3044 | } |
3045 | } else { |
3046 | errorReporter.addErrorOn(assignment.getValue(), kj::str("Missing field name." )); |
3047 | } |
3048 | } |
3049 | } |
3050 | |
3051 | kj::String ValueTranslator::makeNodeName(Schema schema) { |
3052 | schema::Node::Reader proto = schema.getProto(); |
3053 | return kj::str(proto.getDisplayName().slice(proto.getDisplayNamePrefixLength())); |
3054 | } |
3055 | |
3056 | kj::String ValueTranslator::makeTypeName(Type type) { |
3057 | switch (type.which()) { |
3058 | case schema::Type::VOID: return kj::str("Void" ); |
3059 | case schema::Type::BOOL: return kj::str("Bool" ); |
3060 | case schema::Type::INT8: return kj::str("Int8" ); |
3061 | case schema::Type::INT16: return kj::str("Int16" ); |
3062 | case schema::Type::INT32: return kj::str("Int32" ); |
3063 | case schema::Type::INT64: return kj::str("Int64" ); |
3064 | case schema::Type::UINT8: return kj::str("UInt8" ); |
3065 | case schema::Type::UINT16: return kj::str("UInt16" ); |
3066 | case schema::Type::UINT32: return kj::str("UInt32" ); |
3067 | case schema::Type::UINT64: return kj::str("UInt64" ); |
3068 | case schema::Type::FLOAT32: return kj::str("Float32" ); |
3069 | case schema::Type::FLOAT64: return kj::str("Float64" ); |
3070 | case schema::Type::TEXT: return kj::str("Text" ); |
3071 | case schema::Type::DATA: return kj::str("Data" ); |
3072 | case schema::Type::LIST: |
3073 | return kj::str("List(" , makeTypeName(type.asList().getElementType()), ")" ); |
3074 | case schema::Type::ENUM: return makeNodeName(type.asEnum()); |
3075 | case schema::Type::STRUCT: return makeNodeName(type.asStruct()); |
3076 | case schema::Type::INTERFACE: return makeNodeName(type.asInterface()); |
3077 | case schema::Type::ANY_POINTER: return kj::str("AnyPointer" ); |
3078 | } |
3079 | KJ_UNREACHABLE; |
3080 | } |
3081 | |
3082 | kj::Maybe<DynamicValue::Reader> NodeTranslator::readConstant( |
3083 | Expression::Reader source, bool isBootstrap) { |
3084 | // Look up the constant decl. |
3085 | NodeTranslator::BrandedDecl constDecl = nullptr; |
3086 | KJ_IF_MAYBE(decl, compileDeclExpression(source, noImplicitParams())) { |
3087 | constDecl = *decl; |
3088 | } else { |
3089 | // Lookup will have reported an error. |
3090 | return nullptr; |
3091 | } |
3092 | |
3093 | // Is it a constant? |
3094 | if(constDecl.getKind().orDefault(Declaration::FILE) != Declaration::CONST) { |
3095 | errorReporter.addErrorOn(source, |
3096 | kj::str("'" , expressionString(source), "' does not refer to a constant." )); |
3097 | return nullptr; |
3098 | } |
3099 | |
3100 | // Extract the ID and brand. |
3101 | MallocMessageBuilder builder(256); |
3102 | auto constBrand = builder.getRoot<schema::Brand>(); |
3103 | uint64_t id = constDecl.getIdAndFillBrand([&]() { return constBrand; }); |
3104 | |
3105 | // Look up the schema -- we'll need this to compile the constant's type. |
3106 | Schema constSchema; |
3107 | KJ_IF_MAYBE(s, resolver.resolveBootstrapSchema(id, constBrand)) { |
3108 | constSchema = *s; |
3109 | } else { |
3110 | // The constant's schema is broken for reasons already reported. |
3111 | return nullptr; |
3112 | } |
3113 | |
3114 | // If we're bootstrapping, then we know we're expecting a primitive value, so if the |
3115 | // constant turns out to be non-primitive, we'll error out anyway. If we're not |
3116 | // bootstrapping, we may be compiling a non-primitive value and so we need the final |
3117 | // version of the constant to make sure its value is filled in. |
3118 | schema::Node::Reader proto = constSchema.getProto(); |
3119 | if (!isBootstrap) { |
3120 | KJ_IF_MAYBE(finalProto, resolver.resolveFinalSchema(id)) { |
3121 | proto = *finalProto; |
3122 | } else { |
3123 | // The constant's final schema is broken for reasons already reported. |
3124 | return nullptr; |
3125 | } |
3126 | } |
3127 | |
3128 | auto constReader = proto.getConst(); |
3129 | auto dynamicConst = toDynamic(constReader.getValue()); |
3130 | auto constValue = dynamicConst.get(KJ_ASSERT_NONNULL(dynamicConst.which())); |
3131 | |
3132 | if (constValue.getType() == DynamicValue::ANY_POINTER) { |
3133 | // We need to assign an appropriate schema to this pointer. |
3134 | AnyPointer::Reader objValue = constValue.as<AnyPointer>(); |
3135 | |
3136 | auto constType = constSchema.asConst().getType(); |
3137 | switch (constType.which()) { |
3138 | case schema::Type::STRUCT: |
3139 | constValue = objValue.getAs<DynamicStruct>(constType.asStruct()); |
3140 | break; |
3141 | case schema::Type::LIST: |
3142 | constValue = objValue.getAs<DynamicList>(constType.asList()); |
3143 | break; |
3144 | case schema::Type::ANY_POINTER: |
3145 | // Fine as-is. |
3146 | break; |
3147 | default: |
3148 | KJ_FAIL_ASSERT("Unrecognized AnyPointer-typed member of schema::Value." ); |
3149 | break; |
3150 | } |
3151 | } |
3152 | |
3153 | if (source.isRelativeName()) { |
3154 | // A fully unqualified identifier looks like it might refer to a constant visible in the |
3155 | // current scope, but if that's really what the user wanted, we want them to use a |
3156 | // qualified name to make it more obvious. Report an error. |
3157 | KJ_IF_MAYBE(scope, resolver.resolveBootstrapSchema(proto.getScopeId(), |
3158 | schema::Brand::Reader())) { |
3159 | auto scopeReader = scope->getProto(); |
3160 | kj::StringPtr parent; |
3161 | if (scopeReader.isFile()) { |
3162 | parent = "" ; |
3163 | } else { |
3164 | parent = scopeReader.getDisplayName().slice(scopeReader.getDisplayNamePrefixLength()); |
3165 | } |
3166 | kj::StringPtr id = source.getRelativeName().getValue(); |
3167 | |
3168 | errorReporter.addErrorOn(source, kj::str( |
3169 | "Constant names must be qualified to avoid confusion. Please replace '" , |
3170 | expressionString(source), "' with '" , parent, "." , id, |
3171 | "', if that's what you intended." )); |
3172 | } |
3173 | } |
3174 | |
3175 | return constValue; |
3176 | } |
3177 | |
3178 | kj::Maybe<kj::Array<const byte>> NodeTranslator::readEmbed(LocatedText::Reader filename) { |
3179 | KJ_IF_MAYBE(data, resolver.readEmbed(filename.getValue())) { |
3180 | return kj::mv(*data); |
3181 | } else { |
3182 | errorReporter.addErrorOn(filename, |
3183 | kj::str("Couldn't read file for embed: " , filename.getValue())); |
3184 | return nullptr; |
3185 | } |
3186 | } |
3187 | |
3188 | Orphan<List<schema::Annotation>> NodeTranslator::compileAnnotationApplications( |
3189 | List<Declaration::AnnotationApplication>::Reader annotations, |
3190 | kj::StringPtr targetsFlagName) { |
3191 | if (annotations.size() == 0 || !compileAnnotations) { |
3192 | // Return null. |
3193 | return Orphan<List<schema::Annotation>>(); |
3194 | } |
3195 | |
3196 | auto result = orphanage.newOrphan<List<schema::Annotation>>(annotations.size()); |
3197 | auto builder = result.get(); |
3198 | |
3199 | for (uint i = 0; i < annotations.size(); i++) { |
3200 | Declaration::AnnotationApplication::Reader annotation = annotations[i]; |
3201 | schema::Annotation::Builder annotationBuilder = builder[i]; |
3202 | |
3203 | // Set the annotation's value to void in case we fail to produce something better below. |
3204 | annotationBuilder.initValue().setVoid(); |
3205 | |
3206 | auto name = annotation.getName(); |
3207 | KJ_IF_MAYBE(decl, compileDeclExpression(name, noImplicitParams())) { |
3208 | KJ_IF_MAYBE(kind, decl->getKind()) { |
3209 | if (*kind != Declaration::ANNOTATION) { |
3210 | errorReporter.addErrorOn(name, kj::str( |
3211 | "'" , expressionString(name), "' is not an annotation." )); |
3212 | } else { |
3213 | annotationBuilder.setId(decl->getIdAndFillBrand( |
3214 | [&]() { return annotationBuilder.initBrand(); })); |
3215 | KJ_IF_MAYBE(annotationSchema, |
3216 | resolver.resolveBootstrapSchema(annotationBuilder.getId(), |
3217 | annotationBuilder.getBrand())) { |
3218 | auto node = annotationSchema->getProto().getAnnotation(); |
3219 | if (!toDynamic(node).get(targetsFlagName).as<bool>()) { |
3220 | errorReporter.addErrorOn(name, kj::str( |
3221 | "'" , expressionString(name), "' cannot be applied to this kind of declaration." )); |
3222 | } |
3223 | |
3224 | // Interpret the value. |
3225 | auto value = annotation.getValue(); |
3226 | switch (value.which()) { |
3227 | case Declaration::AnnotationApplication::Value::NONE: |
3228 | // No value, i.e. void. |
3229 | if (node.getType().isVoid()) { |
3230 | annotationBuilder.getValue().setVoid(); |
3231 | } else { |
3232 | errorReporter.addErrorOn(name, kj::str( |
3233 | "'" , expressionString(name), "' requires a value." )); |
3234 | compileDefaultDefaultValue(node.getType(), annotationBuilder.getValue()); |
3235 | } |
3236 | break; |
3237 | |
3238 | case Declaration::AnnotationApplication::Value::EXPRESSION: |
3239 | compileBootstrapValue(value.getExpression(), node.getType(), |
3240 | annotationBuilder.getValue(), |
3241 | *annotationSchema); |
3242 | break; |
3243 | } |
3244 | } |
3245 | } |
3246 | } else if (*kind != Declaration::ANNOTATION) { |
3247 | errorReporter.addErrorOn(name, kj::str( |
3248 | "'" , expressionString(name), "' is not an annotation." )); |
3249 | } |
3250 | } |
3251 | } |
3252 | |
3253 | return result; |
3254 | } |
3255 | |
3256 | } // namespace compiler |
3257 | } // namespace capnp |
3258 | |