| // Copyright 2011 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef NINJA_EVAL_ENV_H_ |
| #define NINJA_EVAL_ENV_H_ |
| |
| #include <assert.h> |
| #include <stdint.h> |
| |
| #include <map> |
| #include <mutex> |
| #include <string> |
| #include <unordered_map> |
| #include <vector> |
| using namespace std; |
| |
| #include "hashed_str_view.h" |
| #include "lexer.h" |
| #include "string_piece.h" |
| |
| using DeclIndex = uint32_t; |
| const DeclIndex kLastDeclIndex = static_cast<DeclIndex>(-1); |
| |
| struct Scope; |
| |
| /// Position of a declaration within a scope. |
| struct ScopePosition { |
| ScopePosition(Scope* scope=nullptr, DeclIndex index=0) |
| : scope(scope), index(index) {} |
| |
| Scope* scope = nullptr; |
| DeclIndex index = 0; |
| }; |
| |
| /// Position of a parsed manifest "clump" within its containing scope and within |
| /// all manifest files, in DFS order. This field is updated after parallelized |
| /// parsing is complete. |
| struct BasePosition { |
| BasePosition(ScopePosition scope={}, DeclIndex dfs_location=0) |
| : scope(scope), dfs_location(dfs_location) {} |
| |
| /// The scope location corresponding to this chunk. (Updated during DFS |
| /// manifest parsing.) |
| ScopePosition scope = {}; |
| |
| /// Location of the chunk's declarations within DFS order of all the ninja |
| /// files. This field is useful for verifying that pool references and default |
| /// target references appear *after* their referents have been declared. |
| DeclIndex dfs_location = 0; |
| |
| /// Check that the position is valid. (i.e. The clump it belongs to has been |
| /// assigned a scope and a DFS position.) |
| bool initialized() const { |
| return scope.scope != nullptr; |
| } |
| }; |
| |
| /// Offset of a manifest declaration relative to the beginning of a "clump" of |
| /// declarations. |
| struct RelativePosition { |
| RelativePosition(BasePosition* base=nullptr, DeclIndex offset=0) |
| : base(base), offset(offset) {} |
| |
| BasePosition* base = nullptr; |
| DeclIndex offset = 0; |
| |
| DeclIndex dfs_location() const { Validate(); return base->dfs_location + offset; } |
| DeclIndex scope_index() const { Validate(); return base->scope.index + offset; } |
| Scope* scope() const { Validate(); return base->scope.scope; } |
| ScopePosition scope_pos() const { Validate(); return { base->scope.scope, scope_index() }; } |
| |
| private: |
| void Validate() const { |
| assert(base->initialized() && "clump position hasn't been set yet"); |
| } |
| }; |
| |
| /// An invokable build command and associated metadata (description, etc.). |
| struct Rule { |
| Rule() {} |
| |
| /// This constructor is used to construct built-in rules. |
| explicit Rule(const HashedStrView& name); |
| |
| const std::string& name() const { return name_.str(); } |
| const HashedStr& name_hashed() const { return name_; } |
| |
| static bool IsReservedBinding(StringPiece var); |
| |
| const std::string* GetBinding(const HashedStrView& name) const { |
| for (auto it = bindings_.rbegin(); it != bindings_.rend(); ++it) { |
| if (it->first == name) |
| return &it->second; |
| } |
| return nullptr; |
| } |
| |
| /// Temporary fields used only during manifest parsing. |
| struct { |
| /// The position of the rule in its source file. Used for diagnostics. |
| size_t rule_name_diag_pos = 0; |
| } parse_state_; |
| |
| RelativePosition pos_; |
| HashedStr name_; |
| std::vector<std::pair<HashedStr, std::string>> bindings_; |
| }; |
| |
| struct Binding { |
| /// This function is thread-safe. It stores a copy of the evaluated binding in |
| /// the Binding object if it hasn't been evaluated yet. |
| void Evaluate(std::string* out_append); |
| |
| const std::string& name() { return name_.str(); } |
| const HashedStr& name_hashed() { return name_; } |
| |
| RelativePosition pos_; |
| HashedStr name_; |
| |
| /// The manifest parser initializes this field with the location of the |
| /// binding's unevaluated memory in the loaded manifest file. |
| StringPiece parsed_value_; |
| |
| private: |
| /// This binding's value is evaluated lazily, but it must be evaluated before |
| /// the manifest files are unloaded. |
| std::atomic<bool> is_evaluated_; |
| std::mutex mutex_; |
| std::string final_value_; |
| }; |
| |
| struct Scope { |
| Scope(ScopePosition parent) : parent_(parent) {} |
| |
| /// Preallocate space in the hash tables so adding bindings and rules is more |
| /// efficient. |
| void ReserveTableSpace(size_t new_bindings, size_t new_rules); |
| |
| void AddBinding(Binding* binding) { |
| bindings_[binding->name_hashed()].push_back(binding); |
| } |
| |
| /// Searches for a binding using a scope position (i.e. at parse time). |
| /// Returns nullptr if the binding doesn't exist. |
| static Binding* LookupBindingAtPos(const HashedStrView& var, ScopePosition pos); |
| |
| /// Searches for a binding in a scope and its ancestors. The position of a |
| /// binding within its containing scope is ignored (i.e. post-parse lookup |
| /// semantics). Returns nullptr if no binding is found. The Scope pointer may |
| /// be nullptr. |
| static Binding* LookupBinding(const HashedStrView& var, Scope* scope); |
| |
| /// Append a ${var} reference using a parse-time scope position. |
| static void EvaluateVariableAtPos(std::string* out_append, |
| const HashedStrView& var, |
| ScopePosition pos); |
| |
| /// Append a ${var} reference using a post-parse scope, which may be nullptr. |
| static void EvaluateVariable(std::string* out_append, |
| const HashedStrView& var, |
| Scope* scope); |
| |
| /// Convenience method for looking up the value of a binding after manifest |
| /// parsing is finished. |
| std::string LookupVariable(const HashedStrView& var); |
| |
| bool AddRule(Rule* rule) { |
| return rules_.insert({ rule->name_hashed(), rule }).second; |
| } |
| |
| static Rule* LookupRuleAtPos(const HashedStrView& rule_name, |
| ScopePosition pos); |
| |
| /// Tests use this function to verify that a scope's rules are correct. |
| std::map<std::string, const Rule*> GetRules() const; |
| |
| ScopePosition AllocDecls(DeclIndex count) { |
| ScopePosition result = GetCurrentEndOfScope(); |
| pos_ += count; |
| return result; |
| } |
| |
| ScopePosition GetCurrentEndOfScope() { return { this, pos_ }; } |
| |
| private: |
| /// The position of this scope within its parent scope. |
| /// (ScopePosition::parent will be nullptr for the root scope.) |
| ScopePosition parent_; |
| |
| DeclIndex pos_ = 0; |
| |
| std::unordered_map<HashedStrView, std::vector<Binding*>> bindings_; |
| |
| /// A scope can only declare a rule with a particular name once. Even if a |
| /// scope has a rule of a given name, a lookup for that name can still find a |
| /// parent scope's rule if the search position comes before this scope's rule. |
| std::unordered_map<HashedStrView, Rule*> rules_; |
| }; |
| |
| #endif // NINJA_EVAL_ENV_H_ |