| // |
| // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. |
| // Copyright (C) 2013 LunarG, Inc. |
| // |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions |
| // are met: |
| // |
| // Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // |
| // Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // |
| // Neither the name of 3Dlabs Inc. Ltd. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| // |
| |
| #ifndef _SYMBOL_TABLE_INCLUDED_ |
| #define _SYMBOL_TABLE_INCLUDED_ |
| |
| // |
| // Symbol table for parsing. Has these design characteristics: |
| // |
| // * Same symbol table can be used to compile many shaders, to preserve |
| // effort of creating and loading with the large numbers of built-in |
| // symbols. |
| // |
| // --> This requires a copy mechanism, so initial pools used to create |
| // the shared information can be popped. Done through "clone" |
| // methods. |
| // |
| // * Name mangling will be used to give each function a unique name |
| // so that symbol table lookups are never ambiguous. This allows |
| // a simpler symbol table structure. |
| // |
| // * Pushing and popping of scope, so symbol table will really be a stack |
| // of symbol tables. Searched from the top, with new inserts going into |
| // the top. |
| // |
| // * Constants: Compile time constant symbols will keep their values |
| // in the symbol table. The parser can substitute constants at parse |
| // time, including doing constant folding and constant propagation. |
| // |
| // * No temporaries: Temporaries made from operations (+, --, .xy, etc.) |
| // are tracked in the intermediate representation, not the symbol table. |
| // |
| |
| #include "../Include/Common.h" |
| #include "../Include/intermediate.h" |
| #include "../Include/InfoSink.h" |
| |
| namespace glslang { |
| |
| // |
| // Symbol base class. (Can build functions or variables out of these...) |
| // |
| |
| class TVariable; |
| class TFunction; |
| class TAnonMember; |
| |
| class TSymbol { |
| public: |
| POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) |
| explicit TSymbol(const TString *n) : name(n), numExtensions(0), extensions(0), writable(true) { } |
| virtual TSymbol* clone() const = 0; |
| virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool |
| |
| virtual const TString& getName() const { return *name; } |
| virtual void changeName(const TString* newName) { name = newName; } |
| virtual void addPrefix(const char* prefix) |
| { |
| TString newName(prefix); |
| newName.append(*name); |
| changeName(NewPoolTString(newName.c_str())); |
| } |
| virtual const TString& getMangledName() const { return getName(); } |
| virtual TFunction* getAsFunction() { return 0; } |
| virtual const TFunction* getAsFunction() const { return 0; } |
| virtual TVariable* getAsVariable() { return 0; } |
| virtual const TVariable* getAsVariable() const { return 0; } |
| virtual const TAnonMember* getAsAnonMember() const { return 0; } |
| virtual const TType& getType() const = 0; |
| virtual TType& getWritableType() = 0; |
| virtual void setUniqueId(int id) { uniqueId = id; } |
| virtual int getUniqueId() const { return uniqueId; } |
| virtual void setExtensions(int num, const char* const exts[]) |
| { |
| assert(extensions == 0); |
| assert(num > 0); |
| numExtensions = num; |
| extensions = NewPoolObject(exts[0], num); |
| for (int e = 0; e < num; ++e) |
| extensions[e] = exts[e]; |
| } |
| virtual int getNumExtensions() const { return numExtensions; } |
| virtual const char** getExtensions() const { return extensions; } |
| virtual void dump(TInfoSink &infoSink) const = 0; |
| |
| virtual bool isReadOnly() const { return ! writable; } |
| virtual void makeReadOnly() { writable = false; } |
| |
| protected: |
| explicit TSymbol(const TSymbol&); |
| TSymbol& operator=(const TSymbol&); |
| |
| const TString *name; |
| unsigned int uniqueId; // For cross-scope comparing during code generation |
| |
| // For tracking what extensions must be present |
| // (don't use if correct version/profile is present). |
| int numExtensions; |
| const char** extensions; // an array of pointers to existing constant char strings |
| |
| // |
| // N.B.: Non-const functions that will be generally used should assert on this, |
| // to avoid overwriting shared symbol-table information. |
| // |
| bool writable; |
| }; |
| |
| // |
| // Variable class, meaning a symbol that's not a function. |
| // |
| // There could be a separate class hierarchy for Constant variables; |
| // Only one of int, bool, or float, (or none) is correct for |
| // any particular use, but it's easy to do this way, and doesn't |
| // seem worth having separate classes, and "getConst" can't simply return |
| // different values for different types polymorphically, so this is |
| // just simple and pragmatic. |
| // |
| class TVariable : public TSymbol { |
| public: |
| TVariable(const TString *name, const TType& t, bool uT = false ) |
| : TSymbol(name), |
| userType(uT), |
| constSubtree(nullptr), |
| anonId(-1) { type.shallowCopy(t); } |
| virtual TVariable* clone() const; |
| virtual ~TVariable() { } |
| |
| virtual TVariable* getAsVariable() { return this; } |
| virtual const TVariable* getAsVariable() const { return this; } |
| virtual const TType& getType() const { return type; } |
| virtual TType& getWritableType() { assert(writable); return type; } |
| virtual bool isUserType() const { return userType; } |
| virtual const TConstUnionArray& getConstArray() const { return constArray; } |
| virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } |
| virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } |
| virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } |
| virtual TIntermTyped* getConstSubtree() const { return constSubtree; } |
| virtual void setAnonId(int i) { anonId = i; } |
| virtual int getAnonId() const { return anonId; } |
| |
| virtual void dump(TInfoSink &infoSink) const; |
| |
| protected: |
| explicit TVariable(const TVariable&); |
| TVariable& operator=(const TVariable&); |
| |
| TType type; |
| bool userType; |
| // we are assuming that Pool Allocator will free the memory allocated to unionArray |
| // when this object is destroyed |
| |
| // TODO: these two should be a union |
| // A variable could be a compile-time constant, or a specialization |
| // constant, or neither, but never both. |
| TConstUnionArray constArray; // for compile-time constant value |
| TIntermTyped* constSubtree; // for specialization constant computation |
| int anonId; // the ID used for anonymous blocks: TODO: see if uniqueId could serve a dual purpose |
| }; |
| |
| // |
| // The function sub-class of symbols and the parser will need to |
| // share this definition of a function parameter. |
| // |
| struct TParameter { |
| TString *name; |
| TType* type; |
| TIntermTyped* defaultValue; |
| void copyParam(const TParameter& param) |
| { |
| if (param.name) |
| name = NewPoolTString(param.name->c_str()); |
| else |
| name = 0; |
| type = param.type->clone(); |
| defaultValue = param.defaultValue; |
| } |
| TBuiltInVariable getDeclaredBuiltIn() const { return type->getQualifier().declaredBuiltIn; } |
| }; |
| |
| // |
| // The function sub-class of a symbol. |
| // |
| class TFunction : public TSymbol { |
| public: |
| explicit TFunction(TOperator o) : |
| TSymbol(0), |
| op(o), |
| defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) { } |
| TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) : |
| TSymbol(name), |
| mangledName(*name + '('), |
| op(tOp), |
| defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) |
| { |
| returnType.shallowCopy(retType); |
| declaredBuiltIn = retType.getQualifier().builtIn; |
| } |
| virtual TFunction* clone() const override; |
| virtual ~TFunction(); |
| |
| virtual TFunction* getAsFunction() override { return this; } |
| virtual const TFunction* getAsFunction() const override { return this; } |
| |
| // Install 'p' as the (non-'this') last parameter. |
| // Non-'this' parameters are reflected in both the list of parameters and the |
| // mangled name. |
| virtual void addParameter(TParameter& p) |
| { |
| assert(writable); |
| parameters.push_back(p); |
| p.type->appendMangledName(mangledName); |
| |
| if (p.defaultValue != nullptr) |
| defaultParamCount++; |
| } |
| |
| // Install 'this' as the first parameter. |
| // 'this' is reflected in the list of parameters, but not the mangled name. |
| virtual void addThisParameter(TType& type, const char* name) |
| { |
| TParameter p = { NewPoolTString(name), new TType, nullptr }; |
| p.type->shallowCopy(type); |
| parameters.insert(parameters.begin(), p); |
| } |
| |
| virtual void addPrefix(const char* prefix) override |
| { |
| TSymbol::addPrefix(prefix); |
| mangledName.insert(0, prefix); |
| } |
| |
| virtual void removePrefix(const TString& prefix) |
| { |
| assert(mangledName.compare(0, prefix.size(), prefix) == 0); |
| mangledName.erase(0, prefix.size()); |
| } |
| |
| virtual const TString& getMangledName() const override { return mangledName; } |
| virtual const TType& getType() const override { return returnType; } |
| virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; } |
| virtual TType& getWritableType() override { return returnType; } |
| virtual void relateToOperator(TOperator o) { assert(writable); op = o; } |
| virtual TOperator getBuiltInOp() const { return op; } |
| virtual void setDefined() { assert(writable); defined = true; } |
| virtual bool isDefined() const { return defined; } |
| virtual void setPrototyped() { assert(writable); prototyped = true; } |
| virtual bool isPrototyped() const { return prototyped; } |
| virtual void setImplicitThis() { assert(writable); implicitThis = true; } |
| virtual bool hasImplicitThis() const { return implicitThis; } |
| virtual void setIllegalImplicitThis() { assert(writable); illegalImplicitThis = true; } |
| virtual bool hasIllegalImplicitThis() const { return illegalImplicitThis; } |
| |
| // Return total number of parameters |
| virtual int getParamCount() const { return static_cast<int>(parameters.size()); } |
| // Return number of parameters with default values. |
| virtual int getDefaultParamCount() const { return defaultParamCount; } |
| // Return number of fixed parameters (without default values) |
| virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); } |
| |
| virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; } |
| virtual const TParameter& operator[](int i) const { return parameters[i]; } |
| |
| virtual void dump(TInfoSink &infoSink) const override; |
| |
| protected: |
| explicit TFunction(const TFunction&); |
| TFunction& operator=(const TFunction&); |
| |
| typedef TVector<TParameter> TParamList; |
| TParamList parameters; |
| TType returnType; |
| TBuiltInVariable declaredBuiltIn; |
| |
| TString mangledName; |
| TOperator op; |
| bool defined; |
| bool prototyped; |
| bool implicitThis; // True if this function is allowed to see all members of 'this' |
| bool illegalImplicitThis; // True if this function is not supposed to have access to dynamic members of 'this', |
| // even if it finds member variables in the symbol table. |
| // This is important for a static member function that has member variables in scope, |
| // but is not allowed to use them, or see hidden symbols instead. |
| int defaultParamCount; |
| }; |
| |
| // |
| // Members of anonymous blocks are a kind of TSymbol. They are not hidden in |
| // the symbol table behind a container; rather they are visible and point to |
| // their anonymous container. (The anonymous container is found through the |
| // member, not the other way around.) |
| // |
| class TAnonMember : public TSymbol { |
| public: |
| TAnonMember(const TString* n, unsigned int m, const TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { } |
| virtual TAnonMember* clone() const; |
| virtual ~TAnonMember() { } |
| |
| virtual const TAnonMember* getAsAnonMember() const { return this; } |
| virtual const TVariable& getAnonContainer() const { return anonContainer; } |
| virtual unsigned int getMemberNumber() const { return memberNumber; } |
| |
| virtual const TType& getType() const |
| { |
| const TTypeList& types = *anonContainer.getType().getStruct(); |
| return *types[memberNumber].type; |
| } |
| |
| virtual TType& getWritableType() |
| { |
| assert(writable); |
| const TTypeList& types = *anonContainer.getType().getStruct(); |
| return *types[memberNumber].type; |
| } |
| |
| virtual int getAnonId() const { return anonId; } |
| virtual void dump(TInfoSink &infoSink) const; |
| |
| protected: |
| explicit TAnonMember(const TAnonMember&); |
| TAnonMember& operator=(const TAnonMember&); |
| |
| const TVariable& anonContainer; |
| unsigned int memberNumber; |
| int anonId; |
| }; |
| |
| class TSymbolTableLevel { |
| public: |
| POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) |
| TSymbolTableLevel() : defaultPrecision(0), anonId(0), thisLevel(false) { } |
| ~TSymbolTableLevel(); |
| |
| bool insert(TSymbol& symbol, bool separateNameSpaces) |
| { |
| // |
| // returning true means symbol was added to the table with no semantic errors |
| // |
| const TString& name = symbol.getName(); |
| if (name == "") { |
| symbol.getAsVariable()->setAnonId(anonId++); |
| // An empty name means an anonymous container, exposing its members to the external scope. |
| // Give it a name and insert its members in the symbol table, pointing to the container. |
| char buf[20]; |
| snprintf(buf, 20, "%s%d", AnonymousPrefix, symbol.getAsVariable()->getAnonId()); |
| symbol.changeName(NewPoolTString(buf)); |
| |
| return insertAnonymousMembers(symbol, 0); |
| } else { |
| // Check for redefinition errors: |
| // - STL itself will tell us if there is a direct name collision, with name mangling, at this level |
| // - additionally, check for function-redefining-variable name collisions |
| const TString& insertName = symbol.getMangledName(); |
| if (symbol.getAsFunction()) { |
| // make sure there isn't a variable of this name |
| if (! separateNameSpaces && level.find(name) != level.end()) |
| return false; |
| |
| // insert, and whatever happens is okay |
| level.insert(tLevelPair(insertName, &symbol)); |
| |
| return true; |
| } else |
| return level.insert(tLevelPair(insertName, &symbol)).second; |
| } |
| } |
| |
| // Add more members to an already inserted aggregate object |
| bool amend(TSymbol& symbol, int firstNewMember) |
| { |
| // See insert() for comments on basic explanation of insert. |
| // This operates similarly, but more simply. |
| // Only supporting amend of anonymous blocks so far. |
| if (IsAnonymous(symbol.getName())) |
| return insertAnonymousMembers(symbol, firstNewMember); |
| else |
| return false; |
| } |
| |
| bool insertAnonymousMembers(TSymbol& symbol, int firstMember) |
| { |
| const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); |
| for (unsigned int m = firstMember; m < types.size(); ++m) { |
| TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), symbol.getAsVariable()->getAnonId()); |
| if (! level.insert(tLevelPair(member->getMangledName(), member)).second) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| TSymbol* find(const TString& name) const |
| { |
| tLevel::const_iterator it = level.find(name); |
| if (it == level.end()) |
| return 0; |
| else |
| return (*it).second; |
| } |
| |
| void findFunctionNameList(const TString& name, TVector<const TFunction*>& list) |
| { |
| size_t parenAt = name.find_first_of('('); |
| TString base(name, 0, parenAt + 1); |
| |
| tLevel::const_iterator begin = level.lower_bound(base); |
| base[parenAt] = ')'; // assume ')' is lexically after '(' |
| tLevel::const_iterator end = level.upper_bound(base); |
| for (tLevel::const_iterator it = begin; it != end; ++it) |
| list.push_back(it->second->getAsFunction()); |
| } |
| |
| // See if there is already a function in the table having the given non-function-style name. |
| bool hasFunctionName(const TString& name) const |
| { |
| tLevel::const_iterator candidate = level.lower_bound(name); |
| if (candidate != level.end()) { |
| const TString& candidateName = (*candidate).first; |
| TString::size_type parenAt = candidateName.find_first_of('('); |
| if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // See if there is a variable at this level having the given non-function-style name. |
| // Return true if name is found, and set variable to true if the name was a variable. |
| bool findFunctionVariableName(const TString& name, bool& variable) const |
| { |
| tLevel::const_iterator candidate = level.lower_bound(name); |
| if (candidate != level.end()) { |
| const TString& candidateName = (*candidate).first; |
| TString::size_type parenAt = candidateName.find_first_of('('); |
| if (parenAt == candidateName.npos) { |
| // not a mangled name |
| if (candidateName == name) { |
| // found a variable name match |
| variable = true; |
| return true; |
| } |
| } else { |
| // a mangled name |
| if (candidateName.compare(0, parenAt, name) == 0) { |
| // found a function name match |
| variable = false; |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| // Use this to do a lazy 'push' of precision defaults the first time |
| // a precision statement is seen in a new scope. Leave it at 0 for |
| // when no push was needed. Thus, it is not the current defaults, |
| // it is what to restore the defaults to when popping a level. |
| void setPreviousDefaultPrecisions(const TPrecisionQualifier *p) |
| { |
| // can call multiple times at one scope, will only latch on first call, |
| // as we're tracking the previous scope's values, not the current values |
| if (defaultPrecision != 0) |
| return; |
| |
| defaultPrecision = new TPrecisionQualifier[EbtNumTypes]; |
| for (int t = 0; t < EbtNumTypes; ++t) |
| defaultPrecision[t] = p[t]; |
| } |
| |
| void getPreviousDefaultPrecisions(TPrecisionQualifier *p) |
| { |
| // can be called for table level pops that didn't set the |
| // defaults |
| if (defaultPrecision == 0 || p == 0) |
| return; |
| |
| for (int t = 0; t < EbtNumTypes; ++t) |
| p[t] = defaultPrecision[t]; |
| } |
| |
| void relateToOperator(const char* name, TOperator op); |
| void setFunctionExtensions(const char* name, int num, const char* const extensions[]); |
| void dump(TInfoSink &infoSink) const; |
| TSymbolTableLevel* clone() const; |
| void readOnly(); |
| |
| void setThisLevel() { thisLevel = true; } |
| bool isThisLevel() const { return thisLevel; } |
| |
| protected: |
| explicit TSymbolTableLevel(TSymbolTableLevel&); |
| TSymbolTableLevel& operator=(TSymbolTableLevel&); |
| |
| typedef std::map<TString, TSymbol*, std::less<TString>, pool_allocator<std::pair<const TString, TSymbol*> > > tLevel; |
| typedef const tLevel::value_type tLevelPair; |
| typedef std::pair<tLevel::iterator, bool> tInsertResult; |
| |
| tLevel level; // named mappings |
| TPrecisionQualifier *defaultPrecision; |
| int anonId; |
| bool thisLevel; // True if this level of the symbol table is a structure scope containing member function |
| // that are supposed to see anonymous access to member variables. |
| }; |
| |
| class TSymbolTable { |
| public: |
| TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0) |
| { |
| // |
| // This symbol table cannot be used until push() is called. |
| // |
| } |
| ~TSymbolTable() |
| { |
| // this can be called explicitly; safest to code it so it can be called multiple times |
| |
| // don't deallocate levels passed in from elsewhere |
| while (table.size() > adoptedLevels) |
| pop(0); |
| } |
| |
| void adoptLevels(TSymbolTable& symTable) |
| { |
| for (unsigned int level = 0; level < symTable.table.size(); ++level) { |
| table.push_back(symTable.table[level]); |
| ++adoptedLevels; |
| } |
| uniqueId = symTable.uniqueId; |
| noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; |
| separateNameSpaces = symTable.separateNameSpaces; |
| } |
| |
| // |
| // While level adopting is generic, the methods below enact a the following |
| // convention for levels: |
| // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables |
| // 1: per-stage built-ins, shared across all compiles, but a different copy per stage |
| // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins |
| // 3: user-shader globals |
| // |
| protected: |
| static const int globalLevel = 3; |
| bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels |
| bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals |
| bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals |
| public: |
| bool isEmpty() { return table.size() == 0; } |
| bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } |
| bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } |
| |
| void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } |
| void setSeparateNameSpaces() { separateNameSpaces = true; } |
| |
| void push() |
| { |
| table.push_back(new TSymbolTableLevel); |
| } |
| |
| // Make a new symbol-table level to represent the scope introduced by a structure |
| // containing member functions, such that the member functions can find anonymous |
| // references to member variables. |
| // |
| // 'thisSymbol' should have a name of "" to trigger anonymous structure-member |
| // symbol finds. |
| void pushThis(TSymbol& thisSymbol) |
| { |
| assert(thisSymbol.getName().size() == 0); |
| table.push_back(new TSymbolTableLevel); |
| table.back()->setThisLevel(); |
| insert(thisSymbol); |
| } |
| |
| void pop(TPrecisionQualifier *p) |
| { |
| table[currentLevel()]->getPreviousDefaultPrecisions(p); |
| delete table.back(); |
| table.pop_back(); |
| } |
| |
| // |
| // Insert a visible symbol into the symbol table so it can |
| // be found later by name. |
| // |
| // Returns false if the was a name collision. |
| // |
| bool insert(TSymbol& symbol) |
| { |
| symbol.setUniqueId(++uniqueId); |
| |
| // make sure there isn't a function of this variable name |
| if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName())) |
| return false; |
| |
| // check for not overloading or redefining a built-in function |
| if (noBuiltInRedeclarations) { |
| if (atGlobalLevel() && currentLevel() > 0) { |
| if (table[0]->hasFunctionName(symbol.getName())) |
| return false; |
| if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName())) |
| return false; |
| } |
| } |
| |
| return table[currentLevel()]->insert(symbol, separateNameSpaces); |
| } |
| |
| // Add more members to an already inserted aggregate object |
| bool amend(TSymbol& symbol, int firstNewMember) |
| { |
| // See insert() for comments on basic explanation of insert. |
| // This operates similarly, but more simply. |
| return table[currentLevel()]->amend(symbol, firstNewMember); |
| } |
| |
| // |
| // To allocate an internal temporary, which will need to be uniquely |
| // identified by the consumer of the AST, but never need to |
| // found by doing a symbol table search by name, hence allowed an |
| // arbitrary name in the symbol with no worry of collision. |
| // |
| void makeInternalVariable(TSymbol& symbol) |
| { |
| symbol.setUniqueId(++uniqueId); |
| } |
| |
| // |
| // Copy a variable or anonymous member's structure from a shared level so that |
| // it can be added (soon after return) to the symbol table where it can be |
| // modified without impacting other users of the shared table. |
| // |
| TSymbol* copyUpDeferredInsert(TSymbol* shared) |
| { |
| if (shared->getAsVariable()) { |
| TSymbol* copy = shared->clone(); |
| copy->setUniqueId(shared->getUniqueId()); |
| return copy; |
| } else { |
| const TAnonMember* anon = shared->getAsAnonMember(); |
| assert(anon); |
| TVariable* container = anon->getAnonContainer().clone(); |
| container->changeName(NewPoolTString("")); |
| container->setUniqueId(anon->getAnonContainer().getUniqueId()); |
| return container; |
| } |
| } |
| |
| TSymbol* copyUp(TSymbol* shared) |
| { |
| TSymbol* copy = copyUpDeferredInsert(shared); |
| table[globalLevel]->insert(*copy, separateNameSpaces); |
| if (shared->getAsVariable()) |
| return copy; |
| else { |
| // return the copy of the anonymous member |
| return table[globalLevel]->find(shared->getName()); |
| } |
| } |
| |
| // Normal find of a symbol, that can optionally say whether the symbol was found |
| // at a built-in level or the current top-scope level. |
| TSymbol* find(const TString& name, bool* builtIn = 0, bool* currentScope = 0, int* thisDepthP = 0) |
| { |
| int level = currentLevel(); |
| TSymbol* symbol; |
| int thisDepth = 0; |
| do { |
| if (table[level]->isThisLevel()) |
| ++thisDepth; |
| symbol = table[level]->find(name); |
| --level; |
| } while (symbol == nullptr && level >= 0); |
| level++; |
| if (builtIn) |
| *builtIn = isBuiltInLevel(level); |
| if (currentScope) |
| *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals |
| if (thisDepthP != nullptr) { |
| if (! table[level]->isThisLevel()) |
| thisDepth = 0; |
| *thisDepthP = thisDepth; |
| } |
| |
| return symbol; |
| } |
| |
| // Find of a symbol that returns how many layers deep of nested |
| // structures-with-member-functions ('this' scopes) deep the symbol was |
| // found in. |
| TSymbol* find(const TString& name, int& thisDepth) |
| { |
| int level = currentLevel(); |
| TSymbol* symbol; |
| thisDepth = 0; |
| do { |
| if (table[level]->isThisLevel()) |
| ++thisDepth; |
| symbol = table[level]->find(name); |
| --level; |
| } while (symbol == 0 && level >= 0); |
| |
| if (! table[level + 1]->isThisLevel()) |
| thisDepth = 0; |
| |
| return symbol; |
| } |
| |
| bool isFunctionNameVariable(const TString& name) const |
| { |
| if (separateNameSpaces) |
| return false; |
| |
| int level = currentLevel(); |
| do { |
| bool variable; |
| bool found = table[level]->findFunctionVariableName(name, variable); |
| if (found) |
| return variable; |
| --level; |
| } while (level >= 0); |
| |
| return false; |
| } |
| |
| void findFunctionNameList(const TString& name, TVector<const TFunction*>& list, bool& builtIn) |
| { |
| // For user levels, return the set found in the first scope with a match |
| builtIn = false; |
| int level = currentLevel(); |
| do { |
| table[level]->findFunctionNameList(name, list); |
| --level; |
| } while (list.empty() && level >= globalLevel); |
| |
| if (! list.empty()) |
| return; |
| |
| // Gather across all built-in levels; they don't hide each other |
| builtIn = true; |
| do { |
| table[level]->findFunctionNameList(name, list); |
| --level; |
| } while (level >= 0); |
| } |
| |
| void relateToOperator(const char* name, TOperator op) |
| { |
| for (unsigned int level = 0; level < table.size(); ++level) |
| table[level]->relateToOperator(name, op); |
| } |
| |
| void setFunctionExtensions(const char* name, int num, const char* const extensions[]) |
| { |
| for (unsigned int level = 0; level < table.size(); ++level) |
| table[level]->setFunctionExtensions(name, num, extensions); |
| } |
| |
| void setVariableExtensions(const char* name, int num, const char* const extensions[]) |
| { |
| TSymbol* symbol = find(TString(name)); |
| if (symbol) |
| symbol->setExtensions(num, extensions); |
| } |
| |
| int getMaxSymbolId() { return uniqueId; } |
| void dump(TInfoSink &infoSink) const; |
| void copyTable(const TSymbolTable& copyOf); |
| |
| void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } |
| |
| void readOnly() |
| { |
| for (unsigned int level = 0; level < table.size(); ++level) |
| table[level]->readOnly(); |
| } |
| |
| protected: |
| TSymbolTable(TSymbolTable&); |
| TSymbolTable& operator=(TSymbolTableLevel&); |
| |
| int currentLevel() const { return static_cast<int>(table.size()) - 1; } |
| |
| std::vector<TSymbolTableLevel*> table; |
| int uniqueId; // for unique identification in code generation |
| bool noBuiltInRedeclarations; |
| bool separateNameSpaces; |
| unsigned int adoptedLevels; |
| }; |
| |
| } // end namespace glslang |
| |
| #endif // _SYMBOL_TABLE_INCLUDED_ |