compiler.h 9.47 KB
Newer Older
Kenton Varda's avatar
Kenton Varda committed
1 2
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
Kenton Varda's avatar
Kenton Varda committed
3
//
Kenton Varda's avatar
Kenton Varda committed
4 5 6 7 8 9
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
Kenton Varda's avatar
Kenton Varda committed
10
//
Kenton Varda's avatar
Kenton Varda committed
11 12
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
Kenton Varda's avatar
Kenton Varda committed
13
//
Kenton Varda's avatar
Kenton Varda committed
14 15 16 17 18 19 20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
Kenton Varda's avatar
Kenton Varda committed
21 22 23 24

#ifndef CAPNP_COMPILER_COMPILER_H_
#define CAPNP_COMPILER_COMPILER_H_

25 26 27 28
#if defined(__GNUC__) && !CAPNP_HEADER_WARNINGS
#pragma GCC system_header
#endif

Kenton Varda's avatar
Kenton Varda committed
29
#include <capnp/compiler/grammar.capnp.h>
Kenton Varda's avatar
Kenton Varda committed
30
#include <capnp/schema.capnp.h>
Kenton Varda's avatar
Kenton Varda committed
31 32 33 34 35 36 37 38
#include <capnp/schema-loader.h>
#include "error-reporter.h"

namespace capnp {
namespace compiler {

class Module: public ErrorReporter {
public:
39
  virtual kj::StringPtr getSourceName() = 0;
Kenton Varda's avatar
Kenton Varda committed
40 41 42
  // The name of the module file relative to the source tree.  Used to decide where to output
  // generated code and to form the `displayName` in the schema.

43
  virtual Orphan<ParsedFile> loadContent(Orphanage orphanage) = 0;
Kenton Varda's avatar
Kenton Varda committed
44 45
  // Loads the module content, using the given orphanage to allocate objects if necessary.

46
  virtual kj::Maybe<Module&> importRelative(kj::StringPtr importPath) = 0;
Kenton Varda's avatar
Kenton Varda committed
47 48 49 50 51
  // Find another module, relative to this one.  Importing the same logical module twice should
  // produce the exact same object, comparable by identity.  These objects are owned by some
  // outside pool that outlives the Compiler instance.
};

52
class Compiler: private SchemaLoader::LazyLoadCallback {
Kenton Varda's avatar
Kenton Varda committed
53
  // Cross-links separate modules (schema files) and translates them into schema nodes.
54 55
  //
  // This class is thread-safe, hence all its methods are const.
Kenton Varda's avatar
Kenton Varda committed
56 57

public:
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
  enum AnnotationFlag {
    COMPILE_ANNOTATIONS,
    // Compile annotations normally.

    DROP_ANNOTATIONS
    // Do not compile any annotations, eagerly or lazily.  All "annotations" fields in the schema
    // will be left empty.  This is useful to avoid parsing imports that are used only for
    // annotations which you don't intend to use anyway.
    //
    // Unfortunately annotations cannot simply be compiled lazily because filling in the
    // "annotations" field at the usage site requires knowing the annotation's type, which requires
    // compiling the annotation, and the schema API has no particular way to detect when you
    // try to access the "annotations" field in order to lazily compile the annotations at that
    // point.
  };

  explicit Compiler(AnnotationFlag annotationFlag = COMPILE_ANNOTATIONS);
75
  ~Compiler() noexcept(false);
Kenton Varda's avatar
Kenton Varda committed
76 77
  KJ_DISALLOW_COPY(Compiler);

78
  uint64_t add(Module& module) const;
79 80 81 82 83 84 85 86 87 88 89
  // Add a module to the Compiler, returning the module's file ID.  The ID can then be looked up in
  // the `SchemaLoader` returned by `getLoader()`.  However, the SchemaLoader may behave as if the
  // schema node doesn't exist if any compilation errors occur (reported via the module's
  // ErrorReporter).  The module is parsed at the time `add()` is called, but not fully compiled --
  // individual schema nodes are compiled lazily.  If you want to force eager compilation,
  // see `eagerlyCompile()`, below.

  kj::Maybe<uint64_t> lookup(uint64_t parent, kj::StringPtr childName) const;
  // Given the type ID of a schema node, find the ID of a node nested within it.  Throws an
  // exception if the parent ID is not recognized; returns null if the parent has no child of the
  // given name.  Neither the parent nor the child schema node is actually compiled.
Kenton Varda's avatar
Kenton Varda committed
90

Kenton Varda's avatar
Kenton Varda committed
91
  Orphan<List<schema::CodeGeneratorRequest::RequestedFile::Import>>
92
      getFileImportTable(Module& module, Orphanage orphanage) const;
Kenton Varda's avatar
Kenton Varda committed
93 94
  // Build the import table for the CodeGeneratorRequest for the given module.

95 96 97
  enum Eagerness: uint32_t {
    // Flags specifying how eager to be about compilation.  These are intended to be bitwise OR'd.
    // Used with the method `eagerlyCompile()`.
Kenton Varda's avatar
Kenton Varda committed
98
    //
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    // Schema declarations can be compiled upfront, or they can be compiled lazily as they are
    // needed.  Usually, the difference is not observable, but it is not a perfect abstraction.
    // The difference has the following effects:
    // * `getLoader().getAllLoaded()` only returns the schema nodes which have been compiled so
    //   far.
    // * `getLoader().get()` (i.e. searching for a schema by ID) can only find schema nodes that
    //   have either been compiled already, or which are referenced by schema nodes which have been
    //   compiled already.  This means that if the ID you pass in came from another schema node
    //   compiled with the same compiler, there should be no observable difference, but if you
    //   have an ID from elsewhere which you _a priori_ expect is defined in a particular schema
    //   file, you will need to compile that file eagerly before you look up the node by ID.
    // * Errors are reported when they are encountered, so some errors will not be reported until
    //   the node is actually compiled.
    // * If an imported file is not needed, it will never even be read from disk.
    //
    // The last point is the main reason why you might want to prefer lazy compilation:  it allows
    // you to use a schema file with missing imports, so long as those missing imports are not
    // actually needed.
    //
    // For example, the flag combo:
    //     EAGER_NODE | EAGER_CHILDREN | EAGER_DEPENDENCIES | EAGER_DEPENDENCY_PARENTS
    // will compile the entire given module, plus all direct dependencies of anything in that
    // module, plus all lexical ancestors of those dependencies.  This is what the Cap'n Proto
    // compiler uses when building initial code generator requests.

    ALL_RELATED_NODES = ~0u,
    // Compile everything that is in any way related to the target node, including its entire
    // containing file and everything transitively imported by it.

    NODE = 1 << 0,
    // Eagerly compile the requested node, but not necessarily any of its parents, children, or
    // dependencies.

    PARENTS = 1 << 1,
    // Eagerly compile all lexical parents of the requested node.  Only meaningful in conjuction
    // with NODE.

    CHILDREN = 1 << 2,
    // Eagerly compile all of the node's lexically nested nodes.  Only meaningful in conjuction
    // with NODE.

    DEPENDENCIES = NODE << 15,
    // For all nodes compiled as a result of the above flags, also compile their direct
    // dependencies.  E.g. if Foo is a struct which contains a field of type Bar, and Foo is
    // compiled, then also compile Bar.  "Dependencies" are defined as field types, method
    // parameter and return types, and annotation types.  Nested types and outer types are not
    // considered dependencies.

    DEPENDENCY_PARENTS = PARENTS * DEPENDENCIES,
    DEPENDENCY_CHILDREN = CHILDREN * DEPENDENCIES,
    DEPENDENCY_DEPENDENCIES = DEPENDENCIES * DEPENDENCIES,
    // Like PARENTS, CHILDREN, and DEPENDENCIES, but applies relative to dependency nodes rather
    // than the original requested node.  Note that DEPENDENCY_DEPENDENCIES causes all transitive
    // dependencies of the requested node to be compiled.
    //
    // These flags are defined as multiples of the original flag and DEPENDENCIES so that we
    // can form the flags to use when traversing a dependency by shifting bits.
Kenton Varda's avatar
Kenton Varda committed
156 157
  };

158 159 160 161 162 163 164
  void eagerlyCompile(uint64_t id, uint eagerness) const;
  // Force eager compilation of schema nodes related to the given ID.  `eagerness` specifies which
  // related nodes should be compiled before returning.  It is a bitwise OR of the possible values
  // of the `Eagerness` enum.
  //
  // If this returns and no errors have been reported, then it is guaranteed that the compiled
  // nodes can be found in the SchemaLoader returned by `getLoader()`.
165

166
  const SchemaLoader& getLoader() const { return loader; }
167
  SchemaLoader& getLoader() { return loader; }
Kenton Varda's avatar
Kenton Varda committed
168 169 170
  // Get a SchemaLoader backed by this compiler.  Schema nodes will be lazily constructed as you
  // traverse them using this loader.

171
  void clearWorkspace() const;
172 173 174 175 176 177 178 179
  // The compiler builds a lot of temporary tables and data structures while it works.  It's
  // useful to keep these around if more work is expected (especially if you are using lazy
  // compilation and plan to look up Schema nodes that haven't already been seen), but once
  // the SchemaLoader has everything you need, you can call clearWorkspace() to free up the
  // temporary space.  Note that it's safe to call clearWorkspace() even if you do expect to
  // compile more nodes in the future; it may simply lead to redundant work if the discarded
  // structures are needed again.

Kenton Varda's avatar
Kenton Varda committed
180 181
private:
  class Impl;
182 183
  kj::MutexGuarded<kj::Own<Impl>> impl;
  SchemaLoader loader;
Kenton Varda's avatar
Kenton Varda committed
184 185 186 187

  class CompiledModule;
  class Node;
  class Alias;
188 189

  void load(const SchemaLoader& loader, uint64_t id) const override;
Kenton Varda's avatar
Kenton Varda committed
190 191 192 193 194 195
};

}  // namespace compiler
}  // namespace capnp

#endif  // CAPNP_COMPILER_COMPILER_H_