capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

compiler.h (11900B)


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