| // Copyright 2015 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. | |
| // +build ignore | |
| //#define ENABLE_TID_CHECK | |
| #include "symtab.h" | |
| #ifdef ENABLE_TID_CHECK | |
| #include <pthread.h> | |
| #endif | |
| #include <string.h> | |
| #include <unordered_map> | |
| #include "log.h" | |
| #include "strutil.h" | |
| #include "var.h" | |
| struct SymbolData { | |
| SymbolData() : gv(Var::Undefined()) {} | |
| Var* gv; | |
| }; | |
| vector<string*>* g_symbols; | |
| static vector<SymbolData> g_symbol_data; | |
| Symbol kEmptySym; | |
| Symbol kShellSym; | |
| Symbol kKatiReadonlySym; | |
| Symbol::Symbol(int v) : v_(v) {} | |
| Var* Symbol::PeekGlobalVar() const { | |
| if (static_cast<size_t>(v_) >= g_symbol_data.size()) { | |
| return Var::Undefined(); | |
| } | |
| return g_symbol_data[v_].gv; | |
| } | |
| Var* Symbol::GetGlobalVar() const { | |
| if (static_cast<size_t>(v_) >= g_symbol_data.size()) { | |
| g_symbol_data.resize(v_ + 1); | |
| } | |
| Var* v = g_symbol_data[v_].gv; | |
| if (v->Origin() == VarOrigin::ENVIRONMENT || | |
| v->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE) { | |
| Vars::add_used_env_vars(*this); | |
| } | |
| return v; | |
| } | |
| void Symbol::SetGlobalVar(Var* v, bool is_override, bool* readonly) const { | |
| if (static_cast<size_t>(v_) >= g_symbol_data.size()) { | |
| g_symbol_data.resize(v_ + 1); | |
| } | |
| Var* orig = g_symbol_data[v_].gv; | |
| if (orig->ReadOnly()) { | |
| if (readonly != nullptr) | |
| *readonly = true; | |
| else | |
| ERROR("*** cannot assign to readonly variable: %s", c_str()); | |
| return; | |
| } else if (readonly != nullptr) { | |
| *readonly = false; | |
| } | |
| if (!is_override && (orig->Origin() == VarOrigin::OVERRIDE || | |
| orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE)) { | |
| return; | |
| } | |
| if (orig->Origin() == VarOrigin::COMMAND_LINE && | |
| v->Origin() == VarOrigin::FILE) { | |
| return; | |
| } | |
| if (orig->Origin() == VarOrigin::AUTOMATIC) { | |
| ERROR("overriding automatic variable is not implemented yet"); | |
| } | |
| if (orig->IsDefined()) | |
| delete orig; | |
| g_symbol_data[v_].gv = v; | |
| } | |
| ScopedGlobalVar::ScopedGlobalVar(Symbol name, Var* var) | |
| : name_(name), orig_(NULL) { | |
| orig_ = name.GetGlobalVar(); | |
| g_symbol_data[name_.val()].gv = var; | |
| } | |
| ScopedGlobalVar::~ScopedGlobalVar() { | |
| g_symbol_data[name_.val()].gv = orig_; | |
| } | |
| class Symtab { | |
| public: | |
| Symtab() { | |
| #ifdef ENABLE_TID_CHECK | |
| tid_ = pthread_self(); | |
| #endif | |
| CHECK(g_symbols == NULL); | |
| g_symbols = &symbols_; | |
| Symbol s = InternImpl(""); | |
| CHECK(s.v_ == 0); | |
| CHECK(Intern("") == s); | |
| char b[2]; | |
| b[1] = 0; | |
| for (int i = 1; i < 256; i++) { | |
| b[0] = i; | |
| s = InternImpl(b); | |
| CHECK(s.val() == i); | |
| } | |
| kEmptySym = Intern(""); | |
| kShellSym = Intern("SHELL"); | |
| kKatiReadonlySym = Intern(".KATI_READONLY"); | |
| } | |
| ~Symtab() { | |
| LOG_STAT("%zu symbols", symbols_.size()); | |
| for (string* s : symbols_) | |
| delete s; | |
| } | |
| Symbol InternImpl(StringPiece s) { | |
| auto found = symtab_.find(s); | |
| if (found != symtab_.end()) { | |
| return found->second; | |
| } | |
| symbols_.push_back(new string(s.data(), s.size())); | |
| Symbol sym = Symbol(symtab_.size()); | |
| bool ok = symtab_.emplace(*symbols_.back(), sym).second; | |
| CHECK(ok); | |
| return sym; | |
| } | |
| Symbol Intern(StringPiece s) { | |
| #ifdef ENABLE_TID_CHECK | |
| if (tid_ != pthread_self()) | |
| abort(); | |
| #endif | |
| if (s.size() <= 1) { | |
| return Symbol(s.empty() ? 0 : (unsigned char)s[0]); | |
| } | |
| return InternImpl(s); | |
| } | |
| private: | |
| unordered_map<StringPiece, Symbol> symtab_; | |
| vector<string*> symbols_; | |
| #ifdef ENABLE_TID_CHECK | |
| pthread_t tid_; | |
| #endif | |
| }; | |
| static Symtab* g_symtab; | |
| void InitSymtab() { | |
| g_symtab = new Symtab; | |
| } | |
| void QuitSymtab() { | |
| delete g_symtab; | |
| } | |
| Symbol Intern(StringPiece s) { | |
| return g_symtab->Intern(s); | |
| } | |
| string JoinSymbols(const vector<Symbol>& syms, const char* sep) { | |
| vector<string> strs; | |
| for (Symbol s : syms) { | |
| strs.push_back(s.str()); | |
| } | |
| return JoinStrings(strs, sep); | |
| } |