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 | |
34 | namespace capnp { |
35 | namespace compiler { |
36 | |
37 | typedef std::unordered_map<uint64_t, Orphan<schema::Node::SourceInfo::Reader>> SourceInfoMap; |
38 | |
39 | class Compiler::Alias { |
40 | public: |
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 | |
46 | private: |
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 | |
55 | class 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 | |
65 | public: |
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 | |
107 | private: |
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 | |
234 | class Compiler::CompiledModule { |
235 | public: |
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 | |
251 | private: |
252 | Compiler::Impl& compiler; |
253 | Module& parserModule; |
254 | MallocMessageBuilder contentArena; |
255 | Orphan<ParsedFile> content; |
256 | Node rootNode; |
257 | }; |
258 | |
259 | class Compiler::Impl: public SchemaLoader::LazyLoadCallback { |
260 | public: |
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 | |
329 | private: |
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 | |
359 | kj::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 | |
383 | Compiler::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 | |
404 | Compiler::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 | |
426 | Compiler::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 | |
439 | uint64_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 | |
448 | kj::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 | |
461 | kj::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 | |
607 | kj::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 | } |
628 | kj::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 | } |
637 | void 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 | |
661 | void 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 | |
710 | void 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 | |
773 | void 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 | |
803 | void 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 | |
827 | void 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 | |
839 | void 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 | |
852 | void Compiler::Node::addError(kj::StringPtr error) { |
853 | module->getErrorReporter().addError(startByte, endByte, error); |
854 | } |
855 | |
856 | kj::Maybe<NodeTranslator::Resolver::ResolveResult> |
857 | Compiler::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 | |
886 | kj::Maybe<NodeTranslator::Resolver::ResolveResult> |
887 | Compiler::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 | |
911 | NodeTranslator::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 | |
916 | NodeTranslator::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 | |
922 | kj::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 | |
929 | NodeTranslator::Resolver::ResolvedDecl Compiler::Node::getTopScope() { |
930 | Node& node = module->getRootNode(); |
931 | return ResolvedDecl { node.id, 0, 0, node.kind, &node, nullptr }; |
932 | } |
933 | |
934 | kj::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 | |
949 | kj::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 | |
957 | kj::Maybe<NodeTranslator::Resolver::ResolvedDecl> |
958 | Compiler::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 | |
967 | kj::Maybe<kj::Array<const byte>> Compiler::Node::readEmbed(kj::StringPtr name) { |
968 | return module->embedRelative(name); |
969 | } |
970 | |
971 | kj::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 | |
989 | Compiler::CompiledModule::CompiledModule(Compiler::Impl& compiler, Module& parserModule) |
990 | : compiler(compiler), parserModule(parserModule), |
991 | content(parserModule.loadContent(contentArena.getOrphanage())), |
992 | rootNode(*this) {} |
993 | |
994 | kj::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 | |
1002 | kj::Maybe<kj::Array<const byte>> Compiler::CompiledModule::embedRelative(kj::StringPtr embedPath) { |
1003 | return parserModule.embedRelative(embedPath); |
1004 | } |
1005 | |
1006 | static 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 | |
1051 | static 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 | |
1110 | Orphan<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 | |
1137 | Compiler::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 | |
1168 | Compiler::Impl::~Impl() noexcept(false) {} |
1169 | |
1170 | void 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 | |
1176 | Compiler::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 | |
1185 | uint64_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 | |
1206 | kj::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 | |
1215 | kj::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 | |
1224 | Compiler::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 | |
1230 | uint64_t Compiler::Impl::add(Module& module) { |
1231 | return addInternal(module).getRootNode().getId(); |
1232 | } |
1233 | |
1234 | kj::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 | |
1252 | kj::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 | |
1261 | Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> |
1262 | Compiler::Impl::getFileImportTable(Module& module, Orphanage orphanage) { |
1263 | return addInternal(module).getFileImportTable(orphanage); |
1264 | } |
1265 | |
1266 | Orphan<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 | |
1278 | void 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 | |
1299 | void 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 | |
1309 | void 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 | |
1317 | Compiler::Compiler(AnnotationFlag annotationFlag) |
1318 | : impl(kj::heap<Impl>(annotationFlag)), |
1319 | loader(*this) {} |
1320 | Compiler::~Compiler() noexcept(false) {} |
1321 | |
1322 | uint64_t Compiler::add(Module& module) const { |
1323 | return impl.lockExclusive()->get()->add(module); |
1324 | } |
1325 | |
1326 | kj::Maybe<uint64_t> Compiler::lookup(uint64_t parent, kj::StringPtr childName) const { |
1327 | return impl.lockExclusive()->get()->lookup(parent, childName); |
1328 | } |
1329 | |
1330 | kj::Maybe<schema::Node::SourceInfo::Reader> Compiler::getSourceInfo(uint64_t id) const { |
1331 | return impl.lockExclusive()->get()->getSourceInfo(id); |
1332 | } |
1333 | |
1334 | Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>> |
1335 | Compiler::getFileImportTable(Module& module, Orphanage orphanage) const { |
1336 | return impl.lockExclusive()->get()->getFileImportTable(module, orphanage); |
1337 | } |
1338 | |
1339 | Orphan<List<schema::Node::SourceInfo>> Compiler::getAllSourceInfo(Orphanage orphanage) const { |
1340 | return impl.lockExclusive()->get()->getAllSourceInfo(orphanage); |
1341 | } |
1342 | |
1343 | void Compiler::eagerlyCompile(uint64_t id, uint eagerness) const { |
1344 | impl.lockExclusive()->get()->eagerlyCompile(id, eagerness, loader); |
1345 | } |
1346 | |
1347 | void Compiler::clearWorkspace() const { |
1348 | impl.lockExclusive()->get()->clearWorkspace(); |
1349 | } |
1350 | |
1351 | void 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 | |