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#pragma once
23
24#if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
25#pragma GCC system_header
26#endif
27
28#include <capnp/compiler/grammar.capnp.h>
29#include <capnp/schema.capnp.h>
30#include <capnp/schema-loader.h>
31#include "error-reporter.h"
32
33namespace capnp {
34namespace compiler {
35
36class Module: public ErrorReporter {
37public:
38 virtual kj::StringPtr getSourceName() = 0;
39 // The name of the module file relative to the source tree. Used to decide where to output
40 // generated code and to form the `displayName` in the schema.
41
42 virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) = 0;
43 // Loads the module content, using the given orphanage to allocate objects if necessary.
44
45 virtual kj::Maybe<Module&> importRelative(kj::StringPtr importPath) = 0;
46 // Find another module, relative to this one. Importing the same logical module twice should
47 // produce the exact same object, comparable by identity. These objects are owned by some
48 // outside pool that outlives the Compiler instance.
49
50 virtual kj::Maybe<kj::Array<const byte>> embedRelative(kj::StringPtr embedPath) = 0;
51 // Read and return the content of a file specified using `embed`.
52};
53
54class Compiler final: private SchemaLoader::LazyLoadCallback {
55 // Cross-links separate modules (schema files) and translates them into schema nodes.
56 //
57 // This class is thread-safe, hence all its methods are const.
58
59public:
60 enum AnnotationFlag {
61 COMPILE_ANNOTATIONS,
62 // Compile annotations normally.
63
64 DROP_ANNOTATIONS
65 // Do not compile any annotations, eagerly or lazily. All "annotations" fields in the schema
66 // will be left empty. This is useful to avoid parsing imports that are used only for
67 // annotations which you don't intend to use anyway.
68 //
69 // Unfortunately annotations cannot simply be compiled lazily because filling in the
70 // "annotations" field at the usage site requires knowing the annotation's type, which requires
71 // compiling the annotation, and the schema API has no particular way to detect when you
72 // try to access the "annotations" field in order to lazily compile the annotations at that
73 // point.
74 };
75
76 explicit Compiler(AnnotationFlag annotationFlag = COMPILE_ANNOTATIONS);
77 ~Compiler() noexcept(false);
78 KJ_DISALLOW_COPY(Compiler);
79
80 uint64_t add(Module& module) const;
81 // Add a module to the Compiler, returning the module's file ID. The ID can then be looked up in
82 // the `SchemaLoader` returned by `getLoader()`. However, the SchemaLoader may behave as if the
83 // schema node doesn't exist if any compilation errors occur (reported via the module's
84 // ErrorReporter). The module is parsed at the time `add()` is called, but not fully compiled --
85 // individual schema nodes are compiled lazily. If you want to force eager compilation,
86 // see `eagerlyCompile()`, below.
87
88 kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const;
89 // Given the type ID of a schema node, find the ID of a node nested within it. Throws an
90 // exception if the parent ID is not recognized; returns null if the parent has no child of the
91 // given name. Neither the parent nor the child schema node is actually compiled.
92
93 kj::Maybe<schema::Node::SourceInfo::Reader> getSourceInfo(uint64_t id) const;
94 // Get the SourceInfo for the given type ID, if available.
95
96 Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
97 getFileImportTable(Module& module, Orphanage orphanage) const;
98 // Build the import table for the CodeGeneratorRequest for the given module.
99
100 Orphan<List<schema::Node::SourceInfo>> getAllSourceInfo(Orphanage orphanage) const;
101 // Gets the SourceInfo structs for all nodes parsed by the compiler.
102
103 enum Eagerness: uint32_t {
104 // Flags specifying how eager to be about compilation. These are intended to be bitwise OR'd.
105 // Used with the method `eagerlyCompile()`.
106 //
107 // Schema declarations can be compiled upfront, or they can be compiled lazily as they are
108 // needed. Usually, the difference is not observable, but it is not a perfect abstraction.
109 // The difference has the following effects:
110 // * `getLoader().getAllLoaded()` only returns the schema nodes which have been compiled so
111 // far.
112 // * `getLoader().get()` (i.e. searching for a schema by ID) can only find schema nodes that
113 // have either been compiled already, or which are referenced by schema nodes which have been
114 // compiled already. This means that if the ID you pass in came from another schema node
115 // compiled with the same compiler, there should be no observable difference, but if you
116 // have an ID from elsewhere which you _a priori_ expect is defined in a particular schema
117 // file, you will need to compile that file eagerly before you look up the node by ID.
118 // * Errors are reported when they are encountered, so some errors will not be reported until
119 // the node is actually compiled.
120 // * If an imported file is not needed, it will never even be read from disk.
121 //
122 // The last point is the main reason why you might want to prefer lazy compilation: it allows
123 // you to use a schema file with missing imports, so long as those missing imports are not
124 // actually needed.
125 //
126 // For example, the flag combo:
127 // EAGER_NODE | EAGER_CHILDREN | EAGER_DEPENDENCIES | EAGER_DEPENDENCY_PARENTS
128 // will compile the entire given module, plus all direct dependencies of anything in that
129 // module, plus all lexical ancestors of those dependencies. This is what the Cap'n Proto
130 // compiler uses when building initial code generator requests.
131
132 ALL_RELATED_NODES = ~0u,
133 // Compile everything that is in any way related to the target node, including its entire
134 // containing file and everything transitively imported by it.
135
136 NODE = 1 << 0,
137 // Eagerly compile the requested node, but not necessarily any of its parents, children, or
138 // dependencies.
139
140 PARENTS = 1 << 1,
141 // Eagerly compile all lexical parents of the requested node. Only meaningful in conjuction
142 // with NODE.
143
144 CHILDREN = 1 << 2,
145 // Eagerly compile all of the node's lexically nested nodes. Only meaningful in conjuction
146 // with NODE.
147
148 DEPENDENCIES = NODE << 15,
149 // For all nodes compiled as a result of the above flags, also compile their direct
150 // dependencies. E.g. if Foo is a struct which contains a field of type Bar, and Foo is
151 // compiled, then also compile Bar. "Dependencies" are defined as field types, method
152 // parameter and return types, and annotation types. Nested types and outer types are not
153 // considered dependencies.
154
155 DEPENDENCY_PARENTS = PARENTS * DEPENDENCIES,
156 DEPENDENCY_CHILDREN = CHILDREN * DEPENDENCIES,
157 DEPENDENCY_DEPENDENCIES = DEPENDENCIES * DEPENDENCIES,
158 // Like PARENTS, CHILDREN, and DEPENDENCIES, but applies relative to dependency nodes rather
159 // than the original requested node. Note that DEPENDENCY_DEPENDENCIES causes all transitive
160 // dependencies of the requested node to be compiled.
161 //
162 // These flags are defined as multiples of the original flag and DEPENDENCIES so that we
163 // can form the flags to use when traversing a dependency by shifting bits.
164 };
165
166 void eagerlyCompile(uint64_t id, uint eagerness) const;
167 // Force eager compilation of schema nodes related to the given ID. `eagerness` specifies which
168 // related nodes should be compiled before returning. It is a bitwise OR of the possible values
169 // of the `Eagerness` enum.
170 //
171 // If this returns and no errors have been reported, then it is guaranteed that the compiled
172 // nodes can be found in the SchemaLoader returned by `getLoader()`.
173
174 const SchemaLoader& getLoader() const { return loader; }
175 SchemaLoader& getLoader() { return loader; }
176 // Get a SchemaLoader backed by this compiler. Schema nodes will be lazily constructed as you
177 // traverse them using this loader.
178
179 void clearWorkspace() const;
180 // The compiler builds a lot of temporary tables and data structures while it works. It's
181 // useful to keep these around if more work is expected (especially if you are using lazy
182 // compilation and plan to look up Schema nodes that haven't already been seen), but once
183 // the SchemaLoader has everything you need, you can call clearWorkspace() to free up the
184 // temporary space. Note that it's safe to call clearWorkspace() even if you do expect to
185 // compile more nodes in the future; it may simply lead to redundant work if the discarded
186 // structures are needed again.
187
188private:
189 class Impl;
190 kj::MutexGuarded<kj::Own<Impl>> impl;
191 SchemaLoader loader;
192
193 class CompiledModule;
194 class Node;
195 class Alias;
196
197 void load(const SchemaLoader& loader, uint64_t id) const override;
198};
199
200} // namespace compiler
201} // namespace capnp
202