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 "compiler.h"
23#include "parser.h" // only for generateChildId()
24#include <kj/mutex.h>
25#include <kj/arena.h>
26#include <kj/vector.h>
27#include <kj/debug.h>
28#include <capnp/message.h>
29#include <map>
30#include <set>
31#include <unordered_map>
32#include "node-translator.h"
33
34namespace capnp {
35namespace compiler {
36
37typedef std::unordered_map<uint64_t, Orphan<schema::Node::SourceInfo::Reader>> SourceInfoMap;
38
39class Compiler::Alias {
40public:
41 Alias(CompiledModule& module, Node& parent, const Expression::Reader& targetName)
42 : module(module), parent(parent), targetName(targetName) {}
43
44 kj::Maybe<NodeTranslator::Resolver::ResolveResult> compile();
45
46private:
47 CompiledModule& module;
48 Node& parent;
49 Expression::Reader targetName;
50 kj::Maybe<NodeTranslator::Resolver::ResolveResult> target;
51 Orphan<schema::Brand> brandOrphan;
52 bool initialized = false;
53};
54
55class Compiler::Node final: public NodeTranslator::Resolver {
56 // Passes through four states:
57 // - Stub: On initial construction, the Node is just a placeholder object. Its ID has been
58 // determined, and it is placed in its parent's member table as well as the compiler's
59 // nodes-by-ID table.
60 // - Expanded: Nodes have been constructed for all of this Node's nested children. This happens
61 // the first time a lookup is performed for one of those children.
62 // - Bootstrap: A NodeTranslator has been built and advanced to the bootstrap phase.
63 // - Finished: A final Schema object has been constructed.
64
65public:
66 explicit Node(CompiledModule& module);
67 // Create a root node representing the given file. May
68
69 Node(Node& parent, const Declaration::Reader& declaration);
70 // Create a child node.
71
72 Node(kj::StringPtr name, Declaration::Which kind,
73 List<Declaration::BrandParameter>::Reader genericParams);
74 // Create a dummy node representing a built-in declaration, like "Int32" or "true".
75
76 uint64_t getId() { return id; }
77 uint getParameterCount() { return genericParamCount; }
78 Declaration::Which getKind() { return kind; }
79
80 kj::Maybe<Schema> getBootstrapSchema();
81 kj::Maybe<schema::Node::Reader> getFinalSchema();
82 void loadFinalSchema(const SchemaLoader& loader);
83
84 void traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
85 const SchemaLoader& finalLoader,
86 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
87 // Get the final schema for this node, and also possibly traverse the node's children and
88 // dependencies to ensure that they are loaded, depending on the mode.
89
90 void addError(kj::StringPtr error);
91 // Report an error on this Node.
92
93 // implements NodeTranslator::Resolver -----------------------------
94 kj::Maybe<ResolveResult> resolve(kj::StringPtr name) override;
95 kj::Maybe<ResolveResult> resolveMember(kj::StringPtr name) override;
96 ResolvedDecl resolveBuiltin(Declaration::Which which) override;
97 ResolvedDecl resolveId(uint64_t id) override;
98 kj::Maybe<ResolvedDecl> getParent() override;
99 ResolvedDecl getTopScope() override;
100 kj::Maybe<Schema> resolveBootstrapSchema(
101 uint64_t id, schema::Brand::Reader brand) override;
102 kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) override;
103 kj::Maybe<ResolvedDecl> resolveImport(kj::StringPtr name) override;
104 kj::Maybe<kj::Array<const byte>> readEmbed(kj::StringPtr name) override;
105 kj::Maybe<Type> resolveBootstrapType(schema::Type::Reader type, Schema scope) override;
106
107private:
108 CompiledModule* module; // null iff isBuiltin is true
109 kj::Maybe<Node&> parent;
110
111 Declaration::Reader declaration;
112 // AST of the declaration parsed from the schema file. May become invalid once the content
113 // state has reached FINISHED.
114
115 uint64_t id;
116 // The ID of this node, either taken from the AST or computed based on the parent. Or, a dummy
117 // value, if duplicates were detected.
118
119 kj::StringPtr displayName;
120 // Fully-qualified display name for this node. For files, this is just the file name, otherwise
121 // it is "filename:Path.To.Decl".
122
123 Declaration::Which kind;
124 // Kind of node.
125
126 uint genericParamCount;
127 // Number of generic parameters.
128
129 bool isBuiltin;
130 // Whether this is a bulit-in declaration, like "Int32" or "true".
131
132 uint32_t startByte;
133 uint32_t endByte;
134 // Start and end byte for reporting general errors.
135
136 struct Content {
137 inline Content(): state(STUB) {}
138
139 enum State {
140 STUB,
141 EXPANDED,
142 BOOTSTRAP,
143 FINISHED
144 };
145 State state;
146 // Indicates which fields below are valid.
147
148 inline bool stateHasReached(State minimumState) {
149 return state >= minimumState;
150 }
151 inline void advanceState(State newState) {
152 state = newState;
153 }
154
155 // EXPANDED ------------------------------------
156
157 typedef std::multimap<kj::StringPtr, kj::Own<Node>> NestedNodesMap;
158 NestedNodesMap nestedNodes;
159 kj::Vector<Node*> orderedNestedNodes;
160 // multimap in case of duplicate member names -- we still want to compile them, even if it's an
161 // error.
162
163 typedef std::multimap<kj::StringPtr, kj::Own<Alias>> AliasMap;
164 AliasMap aliases;
165 // The "using" declarations. These are just links to nodes elsewhere.
166
167 // BOOTSTRAP -----------------------------------
168
169 NodeTranslator* translator;
170 // Node translator, allocated in the bootstrap arena.
171
172 kj::Maybe<Schema> bootstrapSchema;
173 // The schema built in the bootstrap loader. Null if the bootstrap loader threw an exception.
174
175 // FINISHED ------------------------------------
176
177 kj::Maybe<schema::Node::Reader> finalSchema;
178 // The completed schema, ready to load into the real schema loader.
179
180 kj::Array<schema::Node::Reader> auxSchemas;
181 // Schemas for all auxiliary nodes built by the NodeTranslator.
182
183 kj::Array<schema::Node::SourceInfo::Reader> sourceInfo;
184 // All source info structs as built by the NodeTranslator.
185 };
186
187 Content guardedContent; // Read using getContent() only!
188 bool inGetContent = false; // True while getContent() is running; detects cycles.
189
190 kj::Maybe<schema::Node::Reader> loadedFinalSchema;
191 // Copy of `finalSchema` as loaded into the final schema loader. This doesn't go away if the
192 // workspace is destroyed.
193
194 // ---------------------------------------------
195
196 static uint64_t generateId(uint64_t parentId, kj::StringPtr declName,
197 Declaration::Id::Reader declId);
198 // Extract the ID from the declaration, or if it has none, generate one based on the name and
199 // parent ID.
200
201 static kj::StringPtr joinDisplayName(kj::Arena& arena, Node& parent, kj::StringPtr declName);
202 // Join the parent's display name with the child's unqualified name to construct the child's
203 // display name.
204
205 kj::Maybe<Content&> getContent(Content::State minimumState);
206 // Advances the content to at least the given state and returns it. Returns null if getContent()
207 // is being called recursively and the given state has not yet been reached, as this indicates
208 // that the declaration recursively depends on itself.
209
210 void traverseNodeDependencies(const schema::Node::Reader& schemaNode, uint eagerness,
211 std::unordered_map<Node*, uint>& seen,
212 const SchemaLoader& finalLoader,
213 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
214 void traverseType(const schema::Type::Reader& type, uint eagerness,
215 std::unordered_map<Node*, uint>& seen,
216 const SchemaLoader& finalLoader,
217 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
218 void traverseBrand(const schema::Brand::Reader& brand, uint eagerness,
219 std::unordered_map<Node*, uint>& seen,
220 const SchemaLoader& finalLoader,
221 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
222 void traverseAnnotations(const List<schema::Annotation>::Reader& annotations, uint eagerness,
223 std::unordered_map<Node*, uint>& seen,
224 const SchemaLoader& finalLoader,
225 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo);
226 void traverseDependency(uint64_t depId, uint eagerness,
227 std::unordered_map<Node*, uint>& seen,
228 const SchemaLoader& finalLoader,
229 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo,
230 bool ignoreIfNotFound = false);
231 // Helpers for traverse().
232};
233
234class Compiler::CompiledModule {
235public:
236 CompiledModule(Compiler::Impl& compiler, Module& parserModule);
237
238 Compiler::Impl& getCompiler() { return compiler; }
239
240 ErrorReporter& getErrorReporter() { return parserModule; }
241 ParsedFile::Reader getParsedFile() { return content.getReader(); }
242 Node& getRootNode() { return rootNode; }
243 kj::StringPtr getSourceName() { return parserModule.getSourceName(); }
244
245 kj::Maybe<CompiledModule&> importRelative(kj::StringPtr importPath);
246 kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr importPath);
247
248 Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
249 getFileImportTable(Orphanage orphanage);
250
251private:
252 Compiler::Impl& compiler;
253 Module& parserModule;
254 MallocMessageBuilder contentArena;
255 Orphan<ParsedFile> content;
256 Node rootNode;
257};
258
259class Compiler::Impl: public SchemaLoader::LazyLoadCallback {
260public:
261 explicit Impl(AnnotationFlag annotationFlag);
262 virtual ~Impl() noexcept(false);
263
264 uint64_t add(Module& module);
265 kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName);
266 kj::Maybe<schema::Node::SourceInfo::Reader> getSourceInfo(uint64_t id);
267 Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
268 getFileImportTable(Module& module, Orphanage orphanage);
269 Orphan<List<schema::Node::SourceInfo>> getAllSourceInfo(Orphanage orphanage);
270 void eagerlyCompile(uint64_t id, uint eagerness, const SchemaLoader& loader);
271 CompiledModule& addInternal(Module& parsedModule);
272
273 struct Workspace {
274 // Scratch space where stuff can be allocated while working. The Workspace is available
275 // whenever nodes are actively being compiled, then is destroyed once control exits the
276 // compiler. Note that since nodes are compiled lazily, a new Workspace may have to be
277 // constructed in order to compile more nodes later.
278
279 MallocMessageBuilder message;
280 Orphanage orphanage;
281 // Orphanage for allocating temporary Cap'n Proto objects.
282
283 kj::Arena arena;
284 // Arena for allocating temporary native objects. Note that objects in `arena` may contain
285 // pointers into `message` that will be manipulated on destruction, so `arena` must be declared
286 // after `message`.
287
288 SchemaLoader bootstrapLoader;
289 // Loader used to load bootstrap schemas. The bootstrap schema nodes are similar to the final
290 // versions except that any value expressions which depend on knowledge of other types (e.g.
291 // default values for struct fields) are left unevaluated (the values in the schema are empty).
292 // These bootstrap schemas can then be plugged into the dynamic API and used to evaluate these
293 // remaining values.
294
295 inline explicit Workspace(const SchemaLoader::LazyLoadCallback& loaderCallback)
296 : orphanage(message.getOrphanage()),
297 bootstrapLoader(loaderCallback) {}
298 };
299
300 kj::Arena& getNodeArena() { return nodeArena; }
301 // Arena where nodes and other permanent objects should be allocated.
302
303 Workspace& getWorkspace() { return workspace; }
304 // Temporary workspace that can be used to construct bootstrap objects.
305
306 inline bool shouldCompileAnnotations() {
307 return annotationFlag == AnnotationFlag::COMPILE_ANNOTATIONS;
308 }
309
310 void clearWorkspace();
311 // Reset the temporary workspace.
312
313 uint64_t addNode(uint64_t desiredId, Node& node);
314 // Add the given node to the by-ID map under the given ID. If another node with the same ID
315 // already exists, choose a new one arbitrarily and use that instead. Return the ID that was
316 // finally used.
317
318 kj::Maybe<Node&> findNode(uint64_t id);
319
320 kj::Maybe<Node&> lookupBuiltin(kj::StringPtr name);
321 Node& getBuiltin(Declaration::Which which);
322
323 void load(const SchemaLoader& loader, uint64_t id) const override;
324 // SchemaLoader callback for the bootstrap loader.
325
326 void loadFinal(const SchemaLoader& loader, uint64_t id);
327 // Called from the SchemaLoader callback for the final loader.
328
329private:
330 AnnotationFlag annotationFlag;
331
332 kj::Arena nodeArena;
333 // Arena used to allocate nodes and other permanent objects.
334
335 std::unordered_map<Module*, kj::Own<CompiledModule>> modules;
336 // Map of parser modules to compiler modules.
337
338 Workspace workspace;
339 // The temporary workspace. This field must be declared after `modules` because objects
340 // allocated in the workspace may hold references to the compiled modules in `modules`.
341
342 std::unordered_map<uint64_t, Node*> nodesById;
343 // Map of nodes by ID.
344
345 std::unordered_map<uint64_t, schema::Node::SourceInfo::Reader> sourceInfoById;
346 // Map of SourceInfos by ID, including SourceInfos for groups and param sturcts (which are not
347 // listed in nodesById).
348
349 std::map<kj::StringPtr, kj::Own<Node>> builtinDecls;
350 std::map<Declaration::Which, Node*> builtinDeclsByKind;
351 // Map of built-in declarations, like "Int32" and "List", which make up the global scope.
352
353 uint64_t nextBogusId = 1000;
354 // Counter for assigning bogus IDs to nodes whose real ID is a duplicate.
355};
356
357// =======================================================================================
358
359kj::Maybe<NodeTranslator::Resolver::ResolveResult> Compiler::Alias::compile() {
360 if (!initialized) {
361 initialized = true;
362
363 auto& workspace = module.getCompiler().getWorkspace();
364 brandOrphan = workspace.orphanage.newOrphan<schema::Brand>();
365
366 // If the Workspace is destroyed, revert the alias to the uninitialized state, because the
367 // orphan we created is no longer valid in this case.
368 workspace.arena.copy(kj::defer([this]() {
369 initialized = false;
370 brandOrphan = Orphan<schema::Brand>();
371 }));
372
373 target = NodeTranslator::compileDecl(
374 parent.getId(), parent.getParameterCount(), parent,
375 module.getErrorReporter(), targetName, brandOrphan.get());
376 }
377
378 return target;
379}
380
381// =======================================================================================
382
383Compiler::Node::Node(CompiledModule& module)
384 : module(&module),
385 parent(nullptr),
386 declaration(module.getParsedFile().getRoot()),
387 id(generateId(0, declaration.getName().getValue(), declaration.getId())),
388 displayName(module.getSourceName()),
389 kind(declaration.which()),
390 genericParamCount(declaration.getParameters().size()),
391 isBuiltin(false) {
392 auto name = declaration.getName();
393 if (name.getValue().size() > 0) {
394 startByte = name.getStartByte();
395 endByte = name.getEndByte();
396 } else {
397 startByte = declaration.getStartByte();
398 endByte = declaration.getEndByte();
399 }
400
401 id = module.getCompiler().addNode(id, *this);
402}
403
404Compiler::Node::Node(Node& parent, const Declaration::Reader& declaration)
405 : module(parent.module),
406 parent(parent),
407 declaration(declaration),
408 id(generateId(parent.id, declaration.getName().getValue(), declaration.getId())),
409 displayName(joinDisplayName(parent.module->getCompiler().getNodeArena(),
410 parent, declaration.getName().getValue())),
411 kind(declaration.which()),
412 genericParamCount(declaration.getParameters().size()),
413 isBuiltin(false) {
414 auto name = declaration.getName();
415 if (name.getValue().size() > 0) {
416 startByte = name.getStartByte();
417 endByte = name.getEndByte();
418 } else {
419 startByte = declaration.getStartByte();
420 endByte = declaration.getEndByte();
421 }
422
423 id = module->getCompiler().addNode(id, *this);
424}
425
426Compiler::Node::Node(kj::StringPtr name, Declaration::Which kind,
427 List<Declaration::BrandParameter>::Reader genericParams)
428 : module(nullptr),
429 parent(nullptr),
430 // It's helpful if these have unique IDs. Real type IDs can't be under 2^31 anyway.
431 id(1000 + static_cast<uint>(kind)),
432 displayName(name),
433 kind(kind),
434 genericParamCount(genericParams.size()),
435 isBuiltin(true),
436 startByte(0),
437 endByte(0) {}
438
439uint64_t Compiler::Node::generateId(uint64_t parentId, kj::StringPtr declName,
440 Declaration::Id::Reader declId) {
441 if (declId.isUid()) {
442 return declId.getUid().getValue();
443 }
444
445 return generateChildId(parentId, declName);
446}
447
448kj::StringPtr Compiler::Node::joinDisplayName(
449 kj::Arena& arena, Node& parent, kj::StringPtr declName) {
450 kj::ArrayPtr<char> result = arena.allocateArray<char>(
451 parent.displayName.size() + declName.size() + 2);
452
453 size_t separatorPos = parent.displayName.size();
454 memcpy(result.begin(), parent.displayName.begin(), separatorPos);
455 result[separatorPos] = parent.parent == nullptr ? ':' : '.';
456 memcpy(result.begin() + separatorPos + 1, declName.begin(), declName.size());
457 result[result.size() - 1] = '\0';
458 return kj::StringPtr(result.begin(), result.size() - 1);
459}
460
461kj::Maybe<Compiler::Node::Content&> Compiler::Node::getContent(Content::State minimumState) {
462 KJ_REQUIRE(!isBuiltin, "illegal method call for built-in declaration");
463
464 auto& content = guardedContent;
465
466 if (content.stateHasReached(minimumState)) {
467 return content;
468 }
469
470 if (inGetContent) {
471 addError("Declaration recursively depends on itself.");
472 return nullptr;
473 }
474
475 inGetContent = true;
476 KJ_DEFER(inGetContent = false);
477
478 switch (content.state) {
479 case Content::STUB: {
480 if (minimumState <= Content::STUB) break;
481
482 // Expand the child nodes.
483 auto& arena = module->getCompiler().getNodeArena();
484
485 for (auto nestedDecl: declaration.getNestedDecls()) {
486 switch (nestedDecl.which()) {
487 case Declaration::FILE:
488 case Declaration::CONST:
489 case Declaration::ANNOTATION:
490 case Declaration::ENUM:
491 case Declaration::STRUCT:
492 case Declaration::INTERFACE: {
493 kj::Own<Node> subNode = arena.allocateOwn<Node>(*this, nestedDecl);
494 kj::StringPtr name = nestedDecl.getName().getValue();
495 content.orderedNestedNodes.add(subNode);
496 content.nestedNodes.insert(std::make_pair(name, kj::mv(subNode)));
497 break;
498 }
499
500 case Declaration::USING: {
501 kj::Own<Alias> alias = arena.allocateOwn<Alias>(
502 *module, *this, nestedDecl.getUsing().getTarget());
503 kj::StringPtr name = nestedDecl.getName().getValue();
504 content.aliases.insert(std::make_pair(name, kj::mv(alias)));
505 break;
506 }
507 case Declaration::ENUMERANT:
508 case Declaration::FIELD:
509 case Declaration::UNION:
510 case Declaration::GROUP:
511 case Declaration::METHOD:
512 case Declaration::NAKED_ID:
513 case Declaration::NAKED_ANNOTATION:
514 // Not a node. Skip.
515 break;
516 default:
517 KJ_FAIL_ASSERT("unknown declaration type", nestedDecl);
518 break;
519 }
520 }
521
522 content.advanceState(Content::EXPANDED);
523 } // fallthrough
524
525 case Content::EXPANDED: {
526 if (minimumState <= Content::EXPANDED) break;
527
528 // Construct the NodeTranslator.
529 auto& workspace = module->getCompiler().getWorkspace();
530
531 auto schemaNode = workspace.orphanage.newOrphan<schema::Node>();
532 auto builder = schemaNode.get();
533 builder.setId(id);
534 builder.setDisplayName(displayName);
535 // TODO(cleanup): Would be better if we could remember the prefix length from before we
536 // added this decl's name to the end.
537 KJ_IF_MAYBE(lastDot, displayName.findLast('.')) {
538 builder.setDisplayNamePrefixLength(*lastDot + 1);
539 }
540 KJ_IF_MAYBE(lastColon, displayName.findLast(':')) {
541 if (*lastColon > builder.getDisplayNamePrefixLength()) {
542 builder.setDisplayNamePrefixLength(*lastColon + 1);
543 }
544 }
545 KJ_IF_MAYBE(p, parent) {
546 builder.setScopeId(p->id);
547 }
548
549 auto nestedNodes = builder.initNestedNodes(content.orderedNestedNodes.size());
550 auto nestedIter = nestedNodes.begin();
551 for (auto node: content.orderedNestedNodes) {
552 nestedIter->setName(node->declaration.getName().getValue());
553 nestedIter->setId(node->id);
554 ++nestedIter;
555 }
556
557 content.translator = &workspace.arena.allocate<NodeTranslator>(
558 *this, module->getErrorReporter(), declaration, kj::mv(schemaNode),
559 module->getCompiler().shouldCompileAnnotations());
560 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
561 auto nodeSet = content.translator->getBootstrapNode();
562 for (auto& auxNode: nodeSet.auxNodes) {
563 workspace.bootstrapLoader.loadOnce(auxNode);
564 }
565 content.bootstrapSchema = workspace.bootstrapLoader.loadOnce(nodeSet.node);
566 })) {
567 content.bootstrapSchema = nullptr;
568 // Only bother to report validation failures if we think we haven't seen any errors.
569 // Otherwise we assume that the errors caused the validation failure.
570 if (!module->getErrorReporter().hadErrors()) {
571 addError(kj::str("Internal compiler bug: Bootstrap schema failed validation:\n",
572 *exception));
573 }
574 }
575
576 // If the Workspace is destroyed, revert the node to the EXPANDED state, because the
577 // NodeTranslator is no longer valid in this case.
578 workspace.arena.copy(kj::defer([&content]() {
579 content.bootstrapSchema = nullptr;
580 if (content.state > Content::EXPANDED) {
581 content.state = Content::EXPANDED;
582 }
583 }));
584
585 content.advanceState(Content::BOOTSTRAP);
586 } // fallthrough
587
588 case Content::BOOTSTRAP: {
589 if (minimumState <= Content::BOOTSTRAP) break;
590
591 // Create the final schema.
592 auto nodeSet = content.translator->finish();
593 content.finalSchema = nodeSet.node;
594 content.auxSchemas = kj::mv(nodeSet.auxNodes);
595 content.sourceInfo = kj::mv(nodeSet.sourceInfo);
596
597 content.advanceState(Content::FINISHED);
598 } // fallthrough
599
600 case Content::FINISHED:
601 break;
602 }
603
604 return content;
605}
606
607kj::Maybe<Schema> Compiler::Node::getBootstrapSchema() {
608 KJ_IF_MAYBE(schema, loadedFinalSchema) {
609 // We don't need to rebuild the bootstrap schema if we already have a final schema.
610 return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(*schema);
611 } else KJ_IF_MAYBE(content, getContent(Content::BOOTSTRAP)) {
612 if (content->state == Content::FINISHED && content->bootstrapSchema == nullptr) {
613 // The bootstrap schema was discarded. Copy it from the final schema.
614 // (We can't just return the final schema because using it could trigger schema loader
615 // callbacks that would deadlock.)
616 KJ_IF_MAYBE(finalSchema, content->finalSchema) {
617 return module->getCompiler().getWorkspace().bootstrapLoader.loadOnce(*finalSchema);
618 } else {
619 return nullptr;
620 }
621 } else {
622 return content->bootstrapSchema;
623 }
624 } else {
625 return nullptr;
626 }
627}
628kj::Maybe<schema::Node::Reader> Compiler::Node::getFinalSchema() {
629 KJ_IF_MAYBE(schema, loadedFinalSchema) {
630 return *schema;
631 } else KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
632 return content->finalSchema;
633 } else {
634 return nullptr;
635 }
636}
637void Compiler::Node::loadFinalSchema(const SchemaLoader& loader) {
638 KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
639 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&](){
640 KJ_IF_MAYBE(finalSchema, content->finalSchema) {
641 KJ_MAP(auxSchema, content->auxSchemas) {
642 return loader.loadOnce(auxSchema);
643 };
644 loadedFinalSchema = loader.loadOnce(*finalSchema).getProto();
645 }
646 })) {
647 // Schema validation threw an exception.
648
649 // Don't try loading this again.
650 content->finalSchema = nullptr;
651
652 // Only bother to report validation failures if we think we haven't seen any errors.
653 // Otherwise we assume that the errors caused the validation failure.
654 if (!module->getErrorReporter().hadErrors()) {
655 addError(kj::str("Internal compiler bug: Schema failed validation:\n", *exception));
656 }
657 }
658 }
659}
660
661void Compiler::Node::traverse(uint eagerness, std::unordered_map<Node*, uint>& seen,
662 const SchemaLoader& finalLoader,
663 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
664 uint& slot = seen[this];
665 if ((slot & eagerness) == eagerness) {
666 // We've already covered this node.
667 return;
668 }
669 slot |= eagerness;
670
671 KJ_IF_MAYBE(content, getContent(Content::FINISHED)) {
672 loadFinalSchema(finalLoader);
673
674 KJ_IF_MAYBE(schema, getFinalSchema()) {
675 if (eagerness / DEPENDENCIES != 0) {
676 // For traversing dependencies, discard the bits lower than DEPENDENCIES and replace
677 // them with the bits above DEPENDENCIES shifted over.
678 uint newEagerness = (eagerness & ~(DEPENDENCIES - 1)) | (eagerness / DEPENDENCIES);
679
680 traverseNodeDependencies(*schema, newEagerness, seen, finalLoader, sourceInfo);
681 for (auto& aux: content->auxSchemas) {
682 traverseNodeDependencies(aux, newEagerness, seen, finalLoader, sourceInfo);
683 }
684 }
685 }
686
687 sourceInfo.addAll(content->sourceInfo);
688 }
689
690 if (eagerness & PARENTS) {
691 KJ_IF_MAYBE(p, parent) {
692 p->traverse(eagerness, seen, finalLoader, sourceInfo);
693 }
694 }
695
696 if (eagerness & CHILDREN) {
697 KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
698 for (auto& child: content->orderedNestedNodes) {
699 child->traverse(eagerness, seen, finalLoader, sourceInfo);
700 }
701
702 // Also traverse `using` declarations.
703 for (auto& child: content->aliases) {
704 child.second->compile();
705 }
706 }
707 }
708}
709
710void Compiler::Node::traverseNodeDependencies(
711 const schema::Node::Reader& schemaNode, uint eagerness,
712 std::unordered_map<Node*, uint>& seen,
713 const SchemaLoader& finalLoader,
714 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
715 switch (schemaNode.which()) {
716 case schema::Node::STRUCT:
717 for (auto field: schemaNode.getStruct().getFields()) {
718 switch (field.which()) {
719 case schema::Field::SLOT:
720 traverseType(field.getSlot().getType(), eagerness, seen, finalLoader, sourceInfo);
721 break;
722 case schema::Field::GROUP:
723 // Aux node will be scanned later.
724 break;
725 }
726
727 traverseAnnotations(field.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
728 }
729 break;
730
731 case schema::Node::ENUM:
732 for (auto enumerant: schemaNode.getEnum().getEnumerants()) {
733 traverseAnnotations(enumerant.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
734 }
735 break;
736
737 case schema::Node::INTERFACE: {
738 auto interface = schemaNode.getInterface();
739 for (auto superclass: interface.getSuperclasses()) {
740 uint64_t superclassId = superclass.getId();
741 if (superclassId != 0) { // if zero, we reported an error earlier
742 traverseDependency(superclassId, eagerness, seen, finalLoader, sourceInfo);
743 }
744 traverseBrand(superclass.getBrand(), eagerness, seen, finalLoader, sourceInfo);
745 }
746 for (auto method: interface.getMethods()) {
747 traverseDependency(
748 method.getParamStructType(), eagerness, seen, finalLoader, sourceInfo, true);
749 traverseBrand(method.getParamBrand(), eagerness, seen, finalLoader, sourceInfo);
750 traverseDependency(
751 method.getResultStructType(), eagerness, seen, finalLoader, sourceInfo, true);
752 traverseBrand(method.getResultBrand(), eagerness, seen, finalLoader, sourceInfo);
753 traverseAnnotations(method.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
754 }
755 break;
756 }
757
758 case schema::Node::CONST:
759 traverseType(schemaNode.getConst().getType(), eagerness, seen, finalLoader, sourceInfo);
760 break;
761
762 case schema::Node::ANNOTATION:
763 traverseType(schemaNode.getAnnotation().getType(), eagerness, seen, finalLoader, sourceInfo);
764 break;
765
766 default:
767 break;
768 }
769
770 traverseAnnotations(schemaNode.getAnnotations(), eagerness, seen, finalLoader, sourceInfo);
771}
772
773void Compiler::Node::traverseType(const schema::Type::Reader& type, uint eagerness,
774 std::unordered_map<Node*, uint>& seen,
775 const SchemaLoader& finalLoader,
776 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
777 uint64_t id = 0;
778 schema::Brand::Reader brand;
779 switch (type.which()) {
780 case schema::Type::STRUCT:
781 id = type.getStruct().getTypeId();
782 brand = type.getStruct().getBrand();
783 break;
784 case schema::Type::ENUM:
785 id = type.getEnum().getTypeId();
786 brand = type.getEnum().getBrand();
787 break;
788 case schema::Type::INTERFACE:
789 id = type.getInterface().getTypeId();
790 brand = type.getInterface().getBrand();
791 break;
792 case schema::Type::LIST:
793 traverseType(type.getList().getElementType(), eagerness, seen, finalLoader, sourceInfo);
794 return;
795 default:
796 return;
797 }
798
799 traverseDependency(id, eagerness, seen, finalLoader, sourceInfo);
800 traverseBrand(brand, eagerness, seen, finalLoader, sourceInfo);
801}
802
803void Compiler::Node::traverseBrand(
804 const schema::Brand::Reader& brand, uint eagerness,
805 std::unordered_map<Node*, uint>& seen,
806 const SchemaLoader& finalLoader,
807 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
808 for (auto scope: brand.getScopes()) {
809 switch (scope.which()) {
810 case schema::Brand::Scope::BIND:
811 for (auto binding: scope.getBind()) {
812 switch (binding.which()) {
813 case schema::Brand::Binding::UNBOUND:
814 break;
815 case schema::Brand::Binding::TYPE:
816 traverseType(binding.getType(), eagerness, seen, finalLoader, sourceInfo);
817 break;
818 }
819 }
820 break;
821 case schema::Brand::Scope::INHERIT:
822 break;
823 }
824 }
825}
826
827void Compiler::Node::traverseDependency(uint64_t depId, uint eagerness,
828 std::unordered_map<Node*, uint>& seen,
829 const SchemaLoader& finalLoader,
830 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo,
831 bool ignoreIfNotFound) {
832 KJ_IF_MAYBE(node, module->getCompiler().findNode(depId)) {
833 node->traverse(eagerness, seen, finalLoader, sourceInfo);
834 } else if (!ignoreIfNotFound) {
835 KJ_FAIL_ASSERT("Dependency ID not present in compiler?", depId);
836 }
837}
838
839void Compiler::Node::traverseAnnotations(const List<schema::Annotation>::Reader& annotations,
840 uint eagerness,
841 std::unordered_map<Node*, uint>& seen,
842 const SchemaLoader& finalLoader,
843 kj::Vector<schema::Node::SourceInfo::Reader>& sourceInfo) {
844 for (auto annotation: annotations) {
845 KJ_IF_MAYBE(node, module->getCompiler().findNode(annotation.getId())) {
846 node->traverse(eagerness, seen, finalLoader, sourceInfo);
847 }
848 }
849}
850
851
852void Compiler::Node::addError(kj::StringPtr error) {
853 module->getErrorReporter().addError(startByte, endByte, error);
854}
855
856kj::Maybe<NodeTranslator::Resolver::ResolveResult>
857Compiler::Node::resolve(kj::StringPtr name) {
858 // Check members.
859 KJ_IF_MAYBE(member, resolveMember(name)) {
860 return *member;
861 }
862
863 // Check parameters.
864 // TODO(perf): Maintain a map?
865 auto params = declaration.getParameters();
866 for (uint i: kj::indices(params)) {
867 if (params[i].getName() == name) {
868 ResolveResult result;
869 result.init<ResolvedParameter>(ResolvedParameter {id, i});
870 return result;
871 }
872 }
873
874 // Check parent scope.
875 KJ_IF_MAYBE(p, parent) {
876 return p->resolve(name);
877 } else KJ_IF_MAYBE(b, module->getCompiler().lookupBuiltin(name)) {
878 ResolveResult result;
879 result.init<ResolvedDecl>(ResolvedDecl { b->id, b->genericParamCount, 0, b->kind, b, nullptr });
880 return result;
881 } else {
882 return nullptr;
883 }
884}
885
886kj::Maybe<NodeTranslator::Resolver::ResolveResult>
887Compiler::Node::resolveMember(kj::StringPtr name) {
888 if (isBuiltin) return nullptr;
889
890 KJ_IF_MAYBE(content, getContent(Content::EXPANDED)) {
891 {
892 auto iter = content->nestedNodes.find(name);
893 if (iter != content->nestedNodes.end()) {
894 Node* node = iter->second;
895 ResolveResult result;
896 result.init<ResolvedDecl>(ResolvedDecl {
897 node->id, node->genericParamCount, id, node->kind, node, nullptr });
898 return result;
899 }
900 }
901 {
902 auto iter = content->aliases.find(name);
903 if (iter != content->aliases.end()) {
904 return iter->second->compile();
905 }
906 }
907 }
908 return nullptr;
909}
910
911NodeTranslator::Resolver::ResolvedDecl Compiler::Node::resolveBuiltin(Declaration::Which which) {
912 auto& b = module->getCompiler().getBuiltin(which);
913 return { b.id, b.genericParamCount, 0, b.kind, &b, nullptr };
914}
915
916NodeTranslator::Resolver::ResolvedDecl Compiler::Node::resolveId(uint64_t id) {
917 auto& n = KJ_ASSERT_NONNULL(module->getCompiler().findNode(id));
918 uint64_t parentId = n.parent.map([](Node& n) { return n.id; }).orDefault(0);
919 return { n.id, n.genericParamCount, parentId, n.kind, &n, nullptr };
920}
921
922kj::Maybe<NodeTranslator::Resolver::ResolvedDecl> Compiler::Node::getParent() {
923 return parent.map([](Node& parent) {
924 uint64_t scopeId = parent.parent.map([](Node& gp) { return gp.id; }).orDefault(0);
925 return ResolvedDecl { parent.id, parent.genericParamCount, scopeId, parent.kind, &parent, nullptr };
926 });
927}
928
929NodeTranslator::Resolver::ResolvedDecl Compiler::Node::getTopScope() {
930 Node& node = module->getRootNode();
931 return ResolvedDecl { node.id, 0, 0, node.kind, &node, nullptr };
932}
933
934kj::Maybe<Schema> Compiler::Node::resolveBootstrapSchema(
935 uint64_t id, schema::Brand::Reader brand) {
936 KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
937 // Make sure the bootstrap schema is loaded into the SchemaLoader.
938 if (node->getBootstrapSchema() == nullptr) {
939 return nullptr;
940 }
941
942 // Now we actually invoke get() to evaluate the brand.
943 return module->getCompiler().getWorkspace().bootstrapLoader.get(id, brand);
944 } else {
945 KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
946 }
947}
948
949kj::Maybe<schema::Node::Reader> Compiler::Node::resolveFinalSchema(uint64_t id) {
950 KJ_IF_MAYBE(node, module->getCompiler().findNode(id)) {
951 return node->getFinalSchema();
952 } else {
953 KJ_FAIL_REQUIRE("Tried to get schema for ID we haven't seen before.");
954 }
955}
956
957kj::Maybe<NodeTranslator::Resolver::ResolvedDecl>
958Compiler::Node::resolveImport(kj::StringPtr name) {
959 KJ_IF_MAYBE(m, module->importRelative(name)) {
960 Node& root = m->getRootNode();
961 return ResolvedDecl { root.id, 0, 0, root.kind, &root, nullptr };
962 } else {
963 return nullptr;
964 }
965}
966
967kj::Maybe<kj::Array<const byte>> Compiler::Node::readEmbed(kj::StringPtr name) {
968 return module->embedRelative(name);
969}
970
971kj::Maybe<Type> Compiler::Node::resolveBootstrapType(schema::Type::Reader type, Schema scope) {
972 // TODO(someday): Arguably should return null if the type or its dependencies are placeholders.
973
974 kj::Maybe<Type> result;
975 KJ_IF_MAYBE(exception, kj::runCatchingExceptions([&]() {
976 result = module->getCompiler().getWorkspace().bootstrapLoader.getType(type, scope);
977 })) {
978 result = nullptr;
979 if (!module->getErrorReporter().hadErrors()) {
980 addError(kj::str("Internal compiler bug: Bootstrap schema failed to load:\n",
981 *exception));
982 }
983 }
984 return result;
985}
986
987// =======================================================================================
988
989Compiler::CompiledModule::CompiledModule(Compiler::Impl& compiler, Module& parserModule)
990 : compiler(compiler), parserModule(parserModule),
991 content(parserModule.loadContent(contentArena.getOrphanage())),
992 rootNode(*this) {}
993
994kj::Maybe<Compiler::CompiledModule&> Compiler::CompiledModule::importRelative(
995 kj::StringPtr importPath) {
996 return parserModule.importRelative(importPath).map(
997 [this](Module& module) -> Compiler::CompiledModule& {
998 return compiler.addInternal(module);
999 });
1000}
1001
1002kj::Maybe<kj::Array<const byte>> Compiler::CompiledModule::embedRelative(kj::StringPtr embedPath) {
1003 return parserModule.embedRelative(embedPath);
1004}
1005
1006static void findImports(Expression::Reader exp, std::set<kj::StringPtr>& output) {
1007 switch (exp.which()) {
1008 case Expression::UNKNOWN:
1009 case Expression::POSITIVE_INT:
1010 case Expression::NEGATIVE_INT:
1011 case Expression::FLOAT:
1012 case Expression::STRING:
1013 case Expression::BINARY:
1014 case Expression::RELATIVE_NAME:
1015 case Expression::ABSOLUTE_NAME:
1016 case Expression::EMBED:
1017 break;
1018
1019 case Expression::IMPORT:
1020 output.insert(exp.getImport().getValue());
1021 break;
1022
1023 case Expression::LIST:
1024 for (auto element: exp.getList()) {
1025 findImports(element, output);
1026 }
1027 break;
1028
1029 case Expression::TUPLE:
1030 for (auto element: exp.getTuple()) {
1031 findImports(element.getValue(), output);
1032 }
1033 break;
1034
1035 case Expression::APPLICATION: {
1036 auto app = exp.getApplication();
1037 findImports(app.getFunction(), output);
1038 for (auto param: app.getParams()) {
1039 findImports(param.getValue(), output);
1040 }
1041 break;
1042 }
1043
1044 case Expression::MEMBER: {
1045 findImports(exp.getMember().getParent(), output);
1046 break;
1047 }
1048 }
1049}
1050
1051static void findImports(Declaration::Reader decl, std::set<kj::StringPtr>& output) {
1052 switch (decl.which()) {
1053 case Declaration::USING:
1054 findImports(decl.getUsing().getTarget(), output);
1055 break;
1056 case Declaration::CONST:
1057 findImports(decl.getConst().getType(), output);
1058 break;
1059 case Declaration::FIELD:
1060 findImports(decl.getField().getType(), output);
1061 break;
1062 case Declaration::INTERFACE:
1063 for (auto superclass: decl.getInterface().getSuperclasses()) {
1064 findImports(superclass, output);
1065 }
1066 break;
1067 case Declaration::METHOD: {
1068 auto method = decl.getMethod();
1069
1070 auto params = method.getParams();
1071 if (params.isNamedList()) {
1072 for (auto param: params.getNamedList()) {
1073 findImports(param.getType(), output);
1074 for (auto ann: param.getAnnotations()) {
1075 findImports(ann.getName(), output);
1076 }
1077 }
1078 } else {
1079 findImports(params.getType(), output);
1080 }
1081
1082 if (method.getResults().isExplicit()) {
1083 auto results = method.getResults().getExplicit();
1084 if (results.isNamedList()) {
1085 for (auto param: results.getNamedList()) {
1086 findImports(param.getType(), output);
1087 for (auto ann: param.getAnnotations()) {
1088 findImports(ann.getName(), output);
1089 }
1090 }
1091 } else {
1092 findImports(results.getType(), output);
1093 }
1094 }
1095 break;
1096 }
1097 default:
1098 break;
1099 }
1100
1101 for (auto ann: decl.getAnnotations()) {
1102 findImports(ann.getName(), output);
1103 }
1104
1105 for (auto nested: decl.getNestedDecls()) {
1106 findImports(nested, output);
1107 }
1108}
1109
1110Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
1111 Compiler::CompiledModule::getFileImportTable(Orphanage orphanage) {
1112 // Build a table of imports for CodeGeneratorRequest.RequestedFile.imports. Note that we only
1113 // care about type imports, not constant value imports, since constant values (including default
1114 // values) are already embedded in full in the schema. In other words, we only need the imports
1115 // that would need to be #included in the generated code.
1116
1117 std::set<kj::StringPtr> importNames;
1118 findImports(content.getReader().getRoot(), importNames);
1119
1120 auto result = orphanage.newOrphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>(
1121 importNames.size());
1122 auto builder = result.get();
1123
1124 uint i = 0;
1125 for (auto name: importNames) {
1126 // We presumably ran this import before, so it shouldn't throw now.
1127 auto entry = builder[i++];
1128 entry.setId(KJ_ASSERT_NONNULL(importRelative(name)).rootNode.getId());
1129 entry.setName(name);
1130 }
1131
1132 return result;
1133}
1134
1135// =======================================================================================
1136
1137Compiler::Impl::Impl(AnnotationFlag annotationFlag)
1138 : annotationFlag(annotationFlag), workspace(*this) {
1139 // Reflectively interpret the members of Declaration.body. Any member prefixed by "builtin"
1140 // defines a builtin declaration visible in the global scope.
1141
1142 StructSchema declSchema = Schema::from<Declaration>();
1143 for (auto field: declSchema.getFields()) {
1144 auto fieldProto = field.getProto();
1145 if (fieldProto.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT) {
1146 auto name = fieldProto.getName();
1147 if (name.startsWith("builtin")) {
1148 kj::StringPtr symbolName = name.slice(strlen("builtin"));
1149
1150 List<Declaration::BrandParameter>::Reader params;
1151 for (auto annotation: fieldProto.getAnnotations()) {
1152 if (annotation.getId() == 0x94099c3f9eb32d6bull) {
1153 params = annotation.getValue().getList().getAs<List<Declaration::BrandParameter>>();
1154 break;
1155 }
1156 }
1157
1158 Declaration::Which which =
1159 static_cast<Declaration::Which>(fieldProto.getDiscriminantValue());
1160 kj::Own<Node> newNode = nodeArena.allocateOwn<Node>(symbolName, which, params);
1161 builtinDeclsByKind[which] = newNode;
1162 builtinDecls[symbolName] = kj::mv(newNode);
1163 }
1164 }
1165 }
1166}
1167
1168Compiler::Impl::~Impl() noexcept(false) {}
1169
1170void Compiler::Impl::clearWorkspace() {
1171 // Make sure we reconstruct the workspace even if destroying it throws an exception.
1172 KJ_DEFER(kj::ctor(workspace, *this));
1173 kj::dtor(workspace);
1174}
1175
1176Compiler::CompiledModule& Compiler::Impl::addInternal(Module& parsedModule) {
1177 kj::Own<CompiledModule>& slot = modules[&parsedModule];
1178 if (slot.get() == nullptr) {
1179 slot = kj::heap<CompiledModule>(*this, parsedModule);
1180 }
1181
1182 return *slot;
1183}
1184
1185uint64_t Compiler::Impl::addNode(uint64_t desiredId, Node& node) {
1186 for (;;) {
1187 auto insertResult = nodesById.insert(std::make_pair(desiredId, &node));
1188 if (insertResult.second) {
1189 return desiredId;
1190 }
1191
1192 // Only report an error if this ID is not bogus. Actual IDs specified in the original source
1193 // code are required to have the upper bit set. Anything else must have been manufactured
1194 // at some point to cover up an error.
1195 if (desiredId & (1ull << 63)) {
1196 node.addError(kj::str("Duplicate ID @0x", kj::hex(desiredId), "."));
1197 insertResult.first->second->addError(
1198 kj::str("ID @0x", kj::hex(desiredId), " originally used here."));
1199 }
1200
1201 // Assign a new bogus ID.
1202 desiredId = nextBogusId++;
1203 }
1204}
1205
1206kj::Maybe<Compiler::Node&> Compiler::Impl::findNode(uint64_t id) {
1207 auto iter = nodesById.find(id);
1208 if (iter == nodesById.end()) {
1209 return nullptr;
1210 } else {
1211 return *iter->second;
1212 }
1213}
1214
1215kj::Maybe<Compiler::Node&> Compiler::Impl::lookupBuiltin(kj::StringPtr name) {
1216 auto iter = builtinDecls.find(name);
1217 if (iter == builtinDecls.end()) {
1218 return nullptr;
1219 } else {
1220 return *iter->second;
1221 }
1222}
1223
1224Compiler::Node& Compiler::Impl::getBuiltin(Declaration::Which which) {
1225 auto iter = builtinDeclsByKind.find(which);
1226 KJ_REQUIRE(iter != builtinDeclsByKind.end(), "invalid builtin", (uint)which);
1227 return *iter->second;
1228}
1229
1230uint64_t Compiler::Impl::add(Module& module) {
1231 return addInternal(module).getRootNode().getId();
1232}
1233
1234kj::Maybe<uint64_t> Compiler::Impl::lookup(uint64_t parent, kj::StringPtr childName) {
1235 // Looking up members does not use the workspace, so we don't need to lock it.
1236 KJ_IF_MAYBE(parentNode, findNode(parent)) {
1237 KJ_IF_MAYBE(child, parentNode->resolveMember(childName)) {
1238 if (child->is<NodeTranslator::Resolver::ResolvedDecl>()) {
1239 return child->get<NodeTranslator::Resolver::ResolvedDecl>().id;
1240 } else {
1241 // An alias. We don't support looking up aliases with this method.
1242 return nullptr;
1243 }
1244 } else {
1245 return nullptr;
1246 }
1247 } else {
1248 KJ_FAIL_REQUIRE("lookup()s parameter 'parent' must be a known ID.", parent);
1249 }
1250}
1251
1252kj::Maybe<schema::Node::SourceInfo::Reader> Compiler::Impl::getSourceInfo(uint64_t id) {
1253 auto iter = sourceInfoById.find(id);
1254 if (iter == sourceInfoById.end()) {
1255 return nullptr;
1256 } else {
1257 return iter->second;
1258 }
1259}
1260
1261Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
1262 Compiler::Impl::getFileImportTable(Module& module, Orphanage orphanage) {
1263 return addInternal(module).getFileImportTable(orphanage);
1264}
1265
1266Orphan<List<schema::Node::SourceInfo>> Compiler::Impl::getAllSourceInfo(Orphanage orphanage) {
1267 auto result = orphanage.newOrphan<List<schema::Node::SourceInfo>>(sourceInfoById.size());
1268
1269 auto builder = result.get();
1270 size_t i = 0;
1271 for (auto& entry: sourceInfoById) {
1272 builder.setWithCaveats(i++, entry.second);
1273 }
1274
1275 return result;
1276}
1277
1278void Compiler::Impl::eagerlyCompile(uint64_t id, uint eagerness,
1279 const SchemaLoader& finalLoader) {
1280 KJ_IF_MAYBE(node, findNode(id)) {
1281 std::unordered_map<Node*, uint> seen;
1282 kj::Vector<schema::Node::SourceInfo::Reader> sourceInfos;
1283 node->traverse(eagerness, seen, finalLoader, sourceInfos);
1284
1285 // Copy the SourceInfo structures into permanent space so that they aren't invalidated when
1286 // clearWorkspace() is called.
1287 for (auto& sourceInfo: sourceInfos) {
1288 auto words = nodeArena.allocateArray<word>(sourceInfo.totalSize().wordCount + 1);
1289 memset(words.begin(), 0, words.asBytes().size());
1290 copyToUnchecked(sourceInfo, words);
1291 sourceInfoById.insert(std::make_pair(sourceInfo.getId(),
1292 readMessageUnchecked<schema::Node::SourceInfo>(words.begin())));
1293 }
1294 } else {
1295 KJ_FAIL_REQUIRE("id did not come from this Compiler.", id);
1296 }
1297}
1298
1299void Compiler::Impl::load(const SchemaLoader& loader, uint64_t id) const {
1300 // We know that this load() is only called from the bootstrap loader which is already protected
1301 // by our mutex, so we can drop thread-safety.
1302 auto& self = const_cast<Compiler::Impl&>(*this);
1303
1304 KJ_IF_MAYBE(node, self.findNode(id)) {
1305 node->getBootstrapSchema();
1306 }
1307}
1308
1309void Compiler::Impl::loadFinal(const SchemaLoader& loader, uint64_t id) {
1310 KJ_IF_MAYBE(node, findNode(id)) {
1311 node->loadFinalSchema(loader);
1312 }
1313}
1314
1315// =======================================================================================
1316
1317Compiler::Compiler(AnnotationFlag annotationFlag)
1318 : impl(kj::heap<Impl>(annotationFlag)),
1319 loader(*this) {}
1320Compiler::~Compiler() noexcept(false) {}
1321
1322uint64_t Compiler::add(Module& module) const {
1323 return impl.lockExclusive()->get()->add(module);
1324}
1325
1326kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const {
1327 return impl.lockExclusive()->get()->lookup(parent, childName);
1328}
1329
1330kj::Maybe<schema::Node::SourceInfo::Reader> Compiler::getSourceInfo(uint64_t id) const {
1331 return impl.lockExclusive()->get()->getSourceInfo(id);
1332}
1333
1334Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
1335 Compiler::getFileImportTable(Module& module, Orphanage orphanage) const {
1336 return impl.lockExclusive()->get()->getFileImportTable(module, orphanage);
1337}
1338
1339Orphan<List<schema::Node::SourceInfo>> Compiler::getAllSourceInfo(Orphanage orphanage) const {
1340 return impl.lockExclusive()->get()->getAllSourceInfo(orphanage);
1341}
1342
1343void Compiler::eagerlyCompile(uint64_t id, uint eagerness) const {
1344 impl.lockExclusive()->get()->eagerlyCompile(id, eagerness, loader);
1345}
1346
1347void Compiler::clearWorkspace() const {
1348 impl.lockExclusive()->get()->clearWorkspace();
1349}
1350
1351void Compiler::load(const SchemaLoader& loader, uint64_t id) const {
1352 impl.lockExclusive()->get()->loadFinal(loader, id);
1353}
1354
1355} // namespace compiler
1356} // namespace capnp
1357