| 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 | |