code: //
  // Copyright (C) 2016-2017 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 GLSLANG_WEB
  
  #include "../Include/Common.h"
  #include "../Include/InfoSink.h"
  
  #include "gl_types.h"
  #include "iomapper.h"
  
  //
  // Map IO bindings.
  //
  // High-level algorithm for one stage:
  //
  // 1. Traverse all code (live+dead) to find the explicitly provided bindings.
  //
  // 2. Traverse (just) the live code to determine which non-provided bindings
  //    require auto-numbering.  We do not auto-number dead ones.
  //
  // 3. Traverse all the code to apply the bindings:
  //    a. explicitly given bindings are offset according to their type
  //    b. implicit live bindings are auto-numbered into the holes, using
  //       any open binding slot.
  //    c. implicit dead bindings are left un-bound.
  //
  
  namespace glslang {
  
  class TVarGatherTraverser : public TLiveTraverser {
  public:
      TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList)
        : TLiveTraverser(i, traverseDeadCode, true, true, false)
        , inputList(inList)
        , outputList(outList)
        , uniformList(uniformList)
      {
      }
  
      virtual void visitSymbol(TIntermSymbol* base)
      {
          TVarLiveMap* target = nullptr;
          if (base->getQualifier().storage == EvqVaryingIn)
              target = &inputList;
          else if (base->getQualifier().storage == EvqVaryingOut)
              target = &outputList;
          else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant())
              target = &uniformList;
          if (target) {
              TVarEntryInfo ent = {base->getId(), base, ! traverseAll};
              ent.stage = intermediate.getStage();
              TVarLiveMap::iterator at = target->find(
                  ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById());
              if (at != target->end() && at->second.id == ent.id)
                  at->second.live = at->second.live || ! traverseAll; // update live state
              else
                  (*target)[ent.symbol->getName()] = ent;
          }
      }
  
  private:
      TVarLiveMap&    inputList;
      TVarLiveMap&    outputList;
      TVarLiveMap&    uniformList;
  };
  
  class TVarSetTraverser : public TLiveTraverser
  {
  public:
      TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList)
        : TLiveTraverser(i, true, true, true, false)
        , inputList(inList)
        , outputList(outList)
        , uniformList(uniformList)
      {
      }
  
      virtual void visitSymbol(TIntermSymbol* base) {
          const TVarLiveMap* source;
          if (base->getQualifier().storage == EvqVaryingIn)
              source = &inputList;
          else if (base->getQualifier().storage == EvqVaryingOut)
              source = &outputList;
          else if (base->getQualifier().isUniformOrBuffer())
              source = &uniformList;
          else
              return;
  
          TVarEntryInfo ent = { base->getId() };
          TVarLiveMap::const_iterator at = source->find(base->getName());
          if (at == source->end())
              return;
  
          if (at->second.id != ent.id)
              return;
  
          if (at->second.newBinding != -1)
              base->getWritableType().getQualifier().layoutBinding = at->second.newBinding;
          if (at->second.newSet != -1)
              base->getWritableType().getQualifier().layoutSet = at->second.newSet;
          if (at->second.newLocation != -1)
              base->getWritableType().getQualifier().layoutLocation = at->second.newLocation;
          if (at->second.newComponent != -1)
              base->getWritableType().getQualifier().layoutComponent = at->second.newComponent;
          if (at->second.newIndex != -1)
              base->getWritableType().getQualifier().layoutIndex = at->second.newIndex;
      }
  
    private:
      const TVarLiveMap&    inputList;
      const TVarLiveMap&    outputList;
      const TVarLiveMap&    uniformList;
  };
  
  struct TNotifyUniformAdaptor
  {
      EShLanguage stage;
      TIoMapResolver& resolver;
      inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r)
        : stage(s)
        , resolver(r)
      {
      }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
      {
          resolver.notifyBinding(stage, entKey.second);
      }
  
  private:
      TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&);
  };
  
  struct TNotifyInOutAdaptor
  {
      EShLanguage stage;
      TIoMapResolver& resolver;
      inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) 
        : stage(s)
        , resolver(r)
      {
      }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
      {
          resolver.notifyInOut(stage, entKey.second);
      }
  
  private:
      TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&);
  };
  
  struct TResolverUniformAdaptor {
      TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
        : stage(s)
        , resolver(r)
        , infoSink(i)
        , error(e)
      {
      }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
          TVarEntryInfo& ent = entKey.second;
          ent.newLocation = -1;
          ent.newComponent = -1;
          ent.newBinding = -1;
          ent.newSet = -1;
          ent.newIndex = -1;
          const bool isValid = resolver.validateBinding(stage, ent);
          if (isValid) {
              resolver.resolveBinding(stage, ent);
              resolver.resolveSet(stage, ent);
              resolver.resolveUniformLocation(stage, ent);
  
              if (ent.newBinding != -1) {
                  if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) {
                      TString err = "mapped binding out of range: " + entKey.first;
  
                      infoSink.info.message(EPrefixInternalError, err.c_str());
                      error = true;
                  }
              }
              if (ent.newSet != -1) {
                  if (ent.newSet >= int(TQualifier::layoutSetEnd)) {
                      TString err = "mapped set out of range: " + entKey.first;
  
                      infoSink.info.message(EPrefixInternalError, err.c_str());
                      error = true;
                  }
              }
          } else {
              TString errorMsg = "Invalid binding: " + entKey.first;
              infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
              error = true;
          }
      }
  
      inline void setStage(EShLanguage s) { stage = s; }
  
      EShLanguage     stage;
      TIoMapResolver& resolver;
      TInfoSink&      infoSink;
      bool&           error;
  
  private:
      TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&);
  };
  
  struct TResolverInOutAdaptor {
      TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e)
        : stage(s)
        , resolver(r)
        , infoSink(i)
        , error(e)
      {
      }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey)
      {
          TVarEntryInfo& ent = entKey.second;
          ent.newLocation = -1;
          ent.newComponent = -1;
          ent.newBinding = -1;
          ent.newSet = -1;
          ent.newIndex = -1;
          const bool isValid = resolver.validateInOut(stage, ent);
          if (isValid) {
              resolver.resolveInOutLocation(stage, ent);
              resolver.resolveInOutComponent(stage, ent);
              resolver.resolveInOutIndex(stage, ent);
          } else {
              TString errorMsg;
              if (ent.symbol->getType().getQualifier().semanticName != nullptr) {
                  errorMsg = "Invalid shader In/Out variable semantic: ";
                  errorMsg += ent.symbol->getType().getQualifier().semanticName;
              } else {
                  errorMsg = "Invalid shader In/Out variable: ";
                  errorMsg += ent.symbol->getName();
              }
              infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
              error = true;
          }
      }
  
      inline void setStage(EShLanguage s) { stage = s; }
  
      EShLanguage     stage;
      TIoMapResolver& resolver;
      TInfoSink&      infoSink;
      bool&           error;
  
  private:
      TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&);
  };
  
  // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings
  
  struct TSymbolValidater
  {
      TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount],
                       TVarLiveMap* uniform[EShLangCount], bool& hadError)
          : preStage(EShLangCount)
          , currentStage(EShLangCount)
          , nextStage(EShLangCount)
          , resolver(r)
          , infoSink(i)
          , hadError(hadError)
      {
          memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*)));
          memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*)));
          memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*)));
      }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
          TVarEntryInfo& ent1 = entKey.second;
          TIntermSymbol* base = ent1.symbol;
          const TType& type = ent1.symbol->getType();
          const TString& name = entKey.first;
          TString mangleName1, mangleName2;
          type.appendMangledName(mangleName1);
          EShLanguage stage = ent1.stage;
          if (currentStage != stage) {
              preStage = currentStage;
              currentStage = stage;
              nextStage = EShLangCount;
              for (int i = currentStage + 1; i < EShLangCount; i++) {
                  if (inVarMaps[i] != nullptr)
                      nextStage = static_cast<EShLanguage>(i);
              }
          }
          if (base->getQualifier().storage == EvqVaryingIn) {
              // validate stage in;
              if (preStage == EShLangCount)
                  return;
              if (outVarMaps[preStage] != nullptr) {
                  auto ent2 = outVarMaps[preStage]->find(name);
                  if (ent2 != outVarMaps[preStage]->end()) {
                      ent2->second.symbol->getType().appendMangledName(mangleName2);
                      if (mangleName1 == mangleName2)
                          return;
                      else {
                          TString err = "Invalid In/Out variable type : " + entKey.first;
                          infoSink.info.message(EPrefixInternalError, err.c_str());
                          hadError = true;
                      }
                  }
                  return;
              }
          } else if (base->getQualifier().storage == EvqVaryingOut) {
              // validate stage out;
              if (nextStage == EShLangCount)
                  return;
              if (outVarMaps[nextStage] != nullptr) {
                  auto ent2 = inVarMaps[nextStage]->find(name);
                  if (ent2 != inVarMaps[nextStage]->end()) {
                      ent2->second.symbol->getType().appendMangledName(mangleName2);
                      if (mangleName1 == mangleName2)
                          return;
                      else {
                          TString err = "Invalid In/Out variable type : " + entKey.first;
                          infoSink.info.message(EPrefixInternalError, err.c_str());
                          hadError = true;
                      }
                  }
                  return;
              }
          } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) {
              // validate uniform type;
              for (int i = 0; i < EShLangCount; i++) {
                  if (i != currentStage && outVarMaps[i] != nullptr) {
                      auto ent2 = uniformVarMap[i]->find(name);
                      if (ent2 != uniformVarMap[i]->end()) {
                          ent2->second.symbol->getType().appendMangledName(mangleName2);
                          if (mangleName1 != mangleName2) {
                              TString err = "Invalid Uniform variable type : " + entKey.first;
                              infoSink.info.message(EPrefixInternalError, err.c_str());
                              hadError = true;
                          }
                          mangleName2.clear();
                      }
                  }
              }
          }
      }
      TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount];
      // Use for mark pre stage, to get more interface symbol information.
      EShLanguage preStage, currentStage, nextStage;
      // Use for mark current shader stage for resolver
      TIoMapResolver& resolver;
      TInfoSink& infoSink;
      bool& hadError;
  
  private:
      TSymbolValidater& operator=(TSymbolValidater&);
  };
  
  struct TSlotCollector {
      TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { }
  
      inline void operator()(std::pair<const TString, TVarEntryInfo>& entKey) {
          resolver.reserverStorageSlot(entKey.second, infoSink);
          resolver.reserverResourceSlot(entKey.second, infoSink);
      }
      TIoMapResolver& resolver;
      TInfoSink& infoSink;
  
  private:
      TSlotCollector& operator=(TSlotCollector&);
  };
  
  TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate)
      : intermediate(intermediate)
      , nextUniformLocation(intermediate.getUniformLocationBase())
      , nextInputLocation(0)
      , nextOutputLocation(0)
  {
      memset(stageMask, false, sizeof(bool) * (EShLangCount + 1));
  }
  
  int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const {
      return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set));
  }
  
  const std::vector<std::string>& TDefaultIoResolverBase::getResourceSetBinding() const {
      return intermediate.getResourceSetBinding();
  }
  
  bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); }
  
  bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); }
  
  TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) {
      return std::lower_bound(slots[set].begin(), slots[set].end(), slot);
  }
  
  bool TDefaultIoResolverBase::checkEmpty(int set, int slot) {
      TSlotSet::iterator at = findSlot(set, slot);
      return ! (at != slots[set].end() && *at == slot);
  }
  
  int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) {
      TSlotSet::iterator at = findSlot(set, slot);
      // tolerate aliasing, by not double-recording aliases
      // (policy about appropriateness of the alias is higher up)
      for (int i = 0; i < size; i++) {
          if (at == slots[set].end() || *at != slot + i)
              at = slots[set].insert(at, slot + i);
          ++at;
      }
      return slot;
  }
  
  int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) {
      TSlotSet::iterator at = findSlot(set, base);
      if (at == slots[set].end())
          return reserveSlot(set, base, size);
      // look for a big enough gap
      for (; at != slots[set].end(); ++at) {
          if (*at - base >= size)
              break;
          base = *at + 1;
      }
      return reserveSlot(set, base, size);
  }
  
  int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      if (type.getQualifier().hasSet()) {
          return ent.newSet = type.getQualifier().layoutSet;
      }
      // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN)
      if (getResourceSetBinding().size() == 1) {
          return ent.newSet = atoi(getResourceSetBinding()[0].c_str());
      }
      return ent.newSet = 0;
  }
  
  int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      const char* name = ent.symbol->getName().c_str();
      // kick out of not doing this
      if (! doAutoLocationMapping()) {
          return ent.newLocation = -1;
      }
      // no locations added if already present, a built-in variable, a block, or an opaque
      if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
          type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
          return ent.newLocation = -1;
      }
      // no locations on blocks of built-in variables
      if (type.isStruct()) {
          if (type.getStruct()->size() < 1) {
              return ent.newLocation = -1;
          }
          if ((*type.getStruct())[0].type->isBuiltIn()) {
              return ent.newLocation = -1;
          }
      }
      int location = intermediate.getUniformLocationOverride(name);
      if (location != -1) {
          return ent.newLocation = location;
      }
      location = nextUniformLocation;
      nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
      return ent.newLocation = location;
  }
  
  int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      // kick out of not doing this
      if (! doAutoLocationMapping()) {
          return ent.newLocation = -1;
      }
  
      // no locations added if already present, or a built-in variable
      if (type.getQualifier().hasLocation() || type.isBuiltIn()) {
          return ent.newLocation = -1;
      }
  
      // no locations on blocks of built-in variables
      if (type.isStruct()) {
          if (type.getStruct()->size() < 1) {
              return ent.newLocation = -1;
          }
          if ((*type.getStruct())[0].type->isBuiltIn()) {
              return ent.newLocation = -1;
          }
      }
      // point to the right input or output location counter
      int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation;
      // Placeholder. This does not do proper cross-stage lining up, nor
      // work with mixed location/no-location declarations.
      int location = nextLocation;
      int typeLocationSize;
      // Don’t take into account the outer-most array if the stage’s
      // interface is automatically an array.
      typeLocationSize = computeTypeLocationSize(type, stage);
      nextLocation += typeLocationSize;
      return ent.newLocation = location;
  }
  
  int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) {
      return ent.newComponent = -1;
  }
  
  int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; }
  
  uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) {
      int typeLocationSize;
      // Don’t take into account the outer-most array if the stage’s
      // interface is automatically an array.
      if (type.getQualifier().isArrayedIo(stage)) {
          TType elementType(type, 0);
          typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage);
      } else {
          typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage);
      }
      return typeLocationSize;
  }
  
  //TDefaultGlslIoResolver
  TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) {
      if (isImageType(type)) {
          return EResImage;
      }
      if (isTextureType(type)) {
          return EResTexture;
      }
      if (isSsboType(type)) {
          return EResSsbo;
      }
      if (isSamplerType(type)) {
          return EResSampler;
      }
      if (isUboType(type)) {
          return EResUbo;
      }
      return EResCount;
  }
  
  TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate)
      : TDefaultIoResolverBase(intermediate)
      , preStage(EShLangCount)
      , currentStage(EShLangCount)
  { }
  
  int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      const TString& name = ent.symbol->getName();
      if (currentStage != stage) {
          preStage = currentStage;
          currentStage = stage;
      }
      // kick out of not doing this
      if (! doAutoLocationMapping()) {
          return ent.newLocation = -1;
      }
      // expand the location to each element if the symbol is a struct or array
      if (type.getQualifier().hasLocation()) {
          return ent.newLocation = type.getQualifier().layoutLocation;
      }
      // no locations added if already present, or a built-in variable
      if (type.isBuiltIn()) {
          return ent.newLocation = -1;
      }
      // no locations on blocks of built-in variables
      if (type.isStruct()) {
          if (type.getStruct()->size() < 1) {
              return ent.newLocation = -1;
          }
          if ((*type.getStruct())[0].type->isBuiltIn()) {
              return ent.newLocation = -1;
          }
      }
      int typeLocationSize = computeTypeLocationSize(type, stage);
      int location = type.getQualifier().layoutLocation;
      bool hasLocation = false;
      EShLanguage keyStage(EShLangCount);
      TStorageQualifier storage;
      storage = EvqInOut;
      if (type.getQualifier().isPipeInput()) {
          // If this symbol is a input, search pre stage's out
          keyStage = preStage;
      }
      if (type.getQualifier().isPipeOutput()) {
          // If this symbol is a output, search next stage's in
          keyStage = currentStage;
      }
      // The in/out in current stage is not declared with location, but it is possible declared
      // with explicit location in other stages, find the storageSlotMap firstly to check whether
      // the in/out has location
      int resourceKey = buildStorageKey(keyStage, storage);
      if (! storageSlotMap[resourceKey].empty()) {
          TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name);
          if (iter != storageSlotMap[resourceKey].end()) {
              // If interface resource be found, set it has location and this symbol's new location
              // equal the symbol's explicit location declarated in pre or next stage.
              //
              // vs:    out vec4 a;
              // fs:    layout(..., location = 3,...) in vec4 a;
              hasLocation = true;
              location = iter->second;
              // if we want deal like that:
              // vs:    layout(location=4) out vec4 a;
              //        out vec4 b;
              //
              // fs:    in vec4 a;
              //        layout(location = 4) in vec4 b;
              // we need retraverse the map.
          }
          if (! hasLocation) {
              // If interface resource note found, It's mean the location in two stage are both implicit declarat.
              // So we should find a new slot for this interface.
              //
              // vs: out vec4 a;
              // fs: in vec4 a;
              location = getFreeSlot(resourceKey, 0, typeLocationSize);
              storageSlotMap[resourceKey][name] = location;
          }
      } else {
          // the first interface declarated in a program.
          TVarSlotMap varSlotMap;
          location = getFreeSlot(resourceKey, 0, typeLocationSize);
          varSlotMap[name] = location;
          storageSlotMap[resourceKey] = varSlotMap;
      }
      //Update location
      return ent.newLocation = location;
  }
  
  int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      const TString& name = ent.symbol->getName();
      // kick out of not doing this
      if (! doAutoLocationMapping()) {
          return ent.newLocation = -1;
      }
      // expand the location to each element if the symbol is a struct or array
      if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) {
          return ent.newLocation = type.getQualifier().layoutLocation;
      } else {
          // no locations added if already present, a built-in variable, a block, or an opaque
          if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock ||
              type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) {
              return ent.newLocation = -1;
          }
          // no locations on blocks of built-in variables
          if (type.isStruct()) {
              if (type.getStruct()->size() < 1) {
                  return ent.newLocation = -1;
              }
              if ((*type.getStruct())[0].type->isBuiltIn()) {
                  return ent.newLocation = -1;
              }
          }
      }
      int location = intermediate.getUniformLocationOverride(name.c_str());
      if (location != -1) {
          return ent.newLocation = location;
      }
  
      int size = TIntermediate::computeTypeUniformLocationSize(type);
  
      // The uniform in current stage is not declared with location, but it is possible declared
      // with explicit location in other stages, find the storageSlotMap firstly to check whether
      // the uniform has location
      bool hasLocation = false;
      int resourceKey = buildStorageKey(EShLangCount, EvqUniform);
      TVarSlotMap& slotMap = storageSlotMap[resourceKey];
      // Check dose shader program has uniform resource
      if (! slotMap.empty()) {
          // If uniform resource not empty, try find a same name uniform
          TVarSlotMap::iterator iter = slotMap.find(name);
          if (iter != slotMap.end()) {
              // If uniform resource be found, set it has location and this symbol's new location
              // equal the uniform's explicit location declarated in other stage.
              //
              // vs:    uniform vec4 a;
              // fs:    layout(..., location = 3,...) uniform vec4 a;
              hasLocation = true;
              location = iter->second;
          }
          if (! hasLocation) {
              // No explicit location declaraten in other stage.
              // So we should find a new slot for this uniform.
              //
              // vs:    uniform vec4 a;
              // fs:    uniform vec4 a;
              location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage));
              storageSlotMap[resourceKey][name] = location;
          }
      } else {
          // the first uniform declarated in a program.
          TVarSlotMap varSlotMap;
          location = getFreeSlot(resourceKey, 0, size);
          varSlotMap[name] = location;
          storageSlotMap[resourceKey] = varSlotMap;
      }
      return ent.newLocation = location;
  }
  
  int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) {
      const TType& type = ent.symbol->getType();
      const TString& name = ent.symbol->getName();
      // On OpenGL arrays of opaque types take a seperate binding for each element
      int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
      TResourceType resource = getResourceType(type);
      // don't need to handle uniform symbol, it will be handled in resolveUniformLocation
      if (resource == EResUbo && type.getBasicType() != EbtBlock) {
          return ent.newBinding = -1;
      }
      // There is no 'set' qualifier in OpenGL shading language, each resource has its own
      // binding name space, so remap the 'set' to resource type which make each resource
      // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS
      int set = resource;
      if (resource < EResCount) {
          if (type.getQualifier().hasBinding()) {
              ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
              return ent.newBinding;
          } else if (ent.live && doAutoBindingMapping()) {
              // The resource in current stage is not declared with binding, but it is possible declared
              // with explicit binding in other stages, find the resourceSlotMap firstly to check whether
              // the resource has binding, don't need to allocate if it already has a binding
              bool hasBinding = false;
              if (! resourceSlotMap[resource].empty()) {
                  TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name);
                  if (iter != resourceSlotMap[resource].end()) {
                      hasBinding = true;
                      ent.newBinding = iter->second;
                  }
              }
              if (! hasBinding) {
                  TVarSlotMap varSlotMap;
                  // find free slot, the caller did make sure it passes all vars with binding
                  // first and now all are passed that do not have a binding and needs one
                  int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings);
                  varSlotMap[name] = binding;
                  resourceSlotMap[resource] = varSlotMap;
                  ent.newBinding = binding;
              }
              return ent.newBinding;
          }
      }
      return ent.newBinding = -1;
  }
  
  void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) {
      // reset stage state
      if (stage == EShLangCount)
          preStage = currentStage = stage;
      // update stage state
      else if (currentStage != stage) {
          preStage = currentStage;
          currentStage = stage;
      }
  }
  
  void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) {
      // TODO nothing
  }
  
  void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) {
      // reset stage state
      if (stage == EShLangCount)
          preStage = currentStage = stage;
      // update stage state
      else if (currentStage != stage) {
          preStage = currentStage;
          currentStage = stage;
      }
  }
  
  void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) {
      // TODO nothing
  }
  
  void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
      const TType& type = ent.symbol->getType();
      const TString& name = ent.symbol->getName();
      TStorageQualifier storage = type.getQualifier().storage;
      EShLanguage stage(EShLangCount);
      switch (storage) {
      case EvqUniform:
          if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) {
              //
              // Reserve the slots for the uniforms who has explicit location
              int storageKey = buildStorageKey(EShLangCount, EvqUniform);
              int location = type.getQualifier().layoutLocation;
              TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
              TVarSlotMap::iterator iter = varSlotMap.find(name);
              if (iter == varSlotMap.end()) {
                  int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
                  reserveSlot(storageKey, location, numLocations);
                  varSlotMap[name] = location;
              } else {
                  // Allocate location by name for OpenGL driver, so the uniform in different
                  // stages should be declared with the same location
                  if (iter->second != location) {
                      TString errorMsg = "Invalid location: " + name;
                      infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
                  }
              }
          }
          break;
      case EvqVaryingIn:
      case EvqVaryingOut:
          //
          // Reserve the slots for the inout who has explicit location
          if (type.getQualifier().hasLocation()) {
              stage = storage == EvqVaryingIn ? preStage : stage;
              stage = storage == EvqVaryingOut ? currentStage : stage;
              int storageKey = buildStorageKey(stage, EvqInOut);
              int location = type.getQualifier().layoutLocation;
              TVarSlotMap& varSlotMap = storageSlotMap[storageKey];
              TVarSlotMap::iterator iter = varSlotMap.find(name);
              if (iter == varSlotMap.end()) {
                  int numLocations = TIntermediate::computeTypeUniformLocationSize(type);
                  reserveSlot(storageKey, location, numLocations);
                  varSlotMap[name] = location;
              } else {
                  // Allocate location by name for OpenGL driver, so the uniform in different
                  // stages should be declared with the same location
                  if (iter->second != location) {
                      TString errorMsg = "Invalid location: " + name;
                      infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
                  }
              }
          }
          break;
      default:
          break;
      }
  }
  
  void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) {
      const TType& type = ent.symbol->getType();
      const TString& name = ent.symbol->getName();
      int resource = getResourceType(type);
      if (type.getQualifier().hasBinding()) {
          TVarSlotMap& varSlotMap = resourceSlotMap[resource];
          TVarSlotMap::iterator iter = varSlotMap.find(name);
          int binding = type.getQualifier().layoutBinding;
          if (iter == varSlotMap.end()) {
              // Reserve the slots for the ubo, ssbo and opaques who has explicit binding
              int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1;
              varSlotMap[name] = binding;
              reserveSlot(resource, binding, numBindings);
          } else {
              // Allocate binding by name for OpenGL driver, so the resource in different
              // stages should be declared with the same binding
              if (iter->second != binding) {
                  TString errorMsg = "Invalid binding: " + name;
                  infoSink.info.message(EPrefixInternalError, errorMsg.c_str());
              }
          }
      }
  }
  
  //TDefaultGlslIoResolver end
  
  /*
   * Basic implementation of glslang::TIoMapResolver that replaces the
   * previous offset behavior.
   * It does the same, uses the offsets for the corresponding uniform
   * types. Also respects the EOptionAutoMapBindings flag and binds
   * them if needed.
   */
  /*
   * Default resolver
   */
  struct TDefaultIoResolver : public TDefaultIoResolverBase {
      TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
  
      bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
  
      TResourceType getResourceType(const glslang::TType& type) override {
          if (isImageType(type)) {
              return EResImage;
          }
          if (isTextureType(type)) {
              return EResTexture;
          }
          if (isSsboType(type)) {
              return EResSsbo;
          }
          if (isSamplerType(type)) {
              return EResSampler;
          }
          if (isUboType(type)) {
              return EResUbo;
          }
          return EResCount;
      }
  
      int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
          const TType& type = ent.symbol->getType();
          const int set = getLayoutSet(type);
          // On OpenGL arrays of opaque types take a seperate binding for each element
          int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1;
          TResourceType resource = getResourceType(type);
          if (resource < EResCount) {
              if (type.getQualifier().hasBinding()) {
                  return ent.newBinding = reserveSlot(
                             set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings);
              } else if (ent.live && doAutoBindingMapping()) {
                  // find free slot, the caller did make sure it passes all vars with binding
                  // first and now all are passed that do not have a binding and needs one
                  return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings);
              }
          }
          return ent.newBinding = -1;
      }
  };
  
  #ifdef ENABLE_HLSL
  /********************************************************************************
  The following IO resolver maps types in HLSL register space, as follows:
  
  t - for shader resource views (SRV)
     TEXTURE1D
     TEXTURE1DARRAY
     TEXTURE2D
     TEXTURE2DARRAY
     TEXTURE3D
     TEXTURECUBE
     TEXTURECUBEARRAY
     TEXTURE2DMS
     TEXTURE2DMSARRAY
     STRUCTUREDBUFFER
     BYTEADDRESSBUFFER
     BUFFER
     TBUFFER
  
  s - for samplers
     SAMPLER
     SAMPLER1D
     SAMPLER2D
     SAMPLER3D
     SAMPLERCUBE
     SAMPLERSTATE
     SAMPLERCOMPARISONSTATE
  
  u - for unordered access views (UAV)
     RWBYTEADDRESSBUFFER
     RWSTRUCTUREDBUFFER
     APPENDSTRUCTUREDBUFFER
     CONSUMESTRUCTUREDBUFFER
     RWBUFFER
     RWTEXTURE1D
     RWTEXTURE1DARRAY
     RWTEXTURE2D
     RWTEXTURE2DARRAY
     RWTEXTURE3D
  
  b - for constant buffer views (CBV)
     CBUFFER
     CONSTANTBUFFER
   ********************************************************************************/
  struct TDefaultHlslIoResolver : public TDefaultIoResolverBase {
      TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { }
  
      bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; }
  
      TResourceType getResourceType(const glslang::TType& type) override {
          if (isUavType(type)) {
              return EResUav;
          }
          if (isSrvType(type)) {
              return EResTexture;
          }
          if (isSamplerType(type)) {
              return EResSampler;
          }
          if (isUboType(type)) {
              return EResUbo;
          }
          return EResCount;
      }
  
      int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override {
          const TType& type = ent.symbol->getType();
          const int set = getLayoutSet(type);
          TResourceType resource = getResourceType(type);
          if (resource < EResCount) {
              if (type.getQualifier().hasBinding()) {
                  return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding);
              } else if (ent.live && doAutoBindingMapping()) {
                  // find free slot, the caller did make sure it passes all vars with binding
                  // first and now all are passed that do not have a binding and needs one
                  return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set));
              }
          }
          return ent.newBinding = -1;
      }
  };
  #endif
  
  // Map I/O variables to provided offsets, and make bindings for
  // unbound but live variables.
  //
  // Returns false if the input is too malformed to do this.
  bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
      bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
                           intermediate.getAutoMapLocations();
      // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
      // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
      for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
          somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
                          intermediate.hasShiftBindingForSet(TResourceType(res));
      }
      if (! somethingToDo && resolver == nullptr)
          return true;
      if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive())
          return false;
      TIntermNode* root = intermediate.getTreeRoot();
      if (root == nullptr)
          return false;
      // if no resolver is provided, use the default resolver with the given shifts and auto map settings
      TDefaultIoResolver defaultResolver(intermediate);
  #ifdef ENABLE_HLSL
      TDefaultHlslIoResolver defaultHlslResolver(intermediate);
      if (resolver == nullptr) {
          // TODO: use a passed in IO mapper for this
          if (intermediate.usingHlslIoMapping())
              resolver = &defaultHlslResolver;
          else
              resolver = &defaultResolver;
      }
      resolver->addStage(stage);
  #else
      resolver = &defaultResolver;
  #endif
  
      TVarLiveMap inVarMap, outVarMap, uniformVarMap;
      TVarLiveVector inVector, outVector, uniformVector;
      TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap);
      TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap);
      root->traverse(&iter_binding_all);
      iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
      while (! iter_binding_live.functions.empty()) {
          TIntermNode* function = iter_binding_live.functions.back();
          iter_binding_live.functions.pop_back();
          function->traverse(&iter_binding_live);
      }
      // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
      std::for_each(inVarMap.begin(), inVarMap.end(),
                    [&inVector](TVarLivePair p) { inVector.push_back(p); });
      std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
          return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
      });
      std::for_each(outVarMap.begin(), outVarMap.end(),
                    [&outVector](TVarLivePair p) { outVector.push_back(p); });
      std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
          return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
      });
      std::for_each(uniformVarMap.begin(), uniformVarMap.end(),
                    [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
      std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
          return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
      });
      bool hadError = false;
      TNotifyInOutAdaptor inOutNotify(stage, *resolver);
      TNotifyUniformAdaptor uniformNotify(stage, *resolver);
      TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError);
      TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError);
      resolver->beginNotifications(stage);
      std::for_each(inVector.begin(), inVector.end(), inOutNotify);
      std::for_each(outVector.begin(), outVector.end(), inOutNotify);
      std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify);
      resolver->endNotifications(stage);
      resolver->beginResolve(stage);
      std::for_each(inVector.begin(), inVector.end(), inOutResolve);
      std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) {
          auto at = inVarMap.find(p.second.symbol->getName());
          if (at != inVarMap.end())
              at->second = p.second;
      });
      std::for_each(outVector.begin(), outVector.end(), inOutResolve);
      std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) {
          auto at = outVarMap.find(p.second.symbol->getName());
          if (at != outVarMap.end())
              at->second = p.second;
      });
      std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
      std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) {
          auto at = uniformVarMap.find(p.second.symbol->getName());
          if (at != uniformVarMap.end())
              at->second = p.second;
      });
      resolver->endResolve(stage);
      if (!hadError) {
          TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap);
          root->traverse(&iter_iomap);
      }
      return !hadError;
  }
  
  // Map I/O variables to provided offsets, and make bindings for
  // unbound but live variables.
  //
  // Returns false if the input is too malformed to do this.
  bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) {
  
      bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() ||
                           intermediate.getAutoMapLocations();
      // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce
      // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true.
      for (int res = 0; (res < EResCount && !somethingToDo); ++res) {
          somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) ||
                          intermediate.hasShiftBindingForSet(TResourceType(res));
      }
      if (! somethingToDo && resolver == nullptr) {
          return true;
      }
      if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) {
          return false;
      }
      TIntermNode* root = intermediate.getTreeRoot();
      if (root == nullptr) {
          return false;
      }
      // if no resolver is provided, use the default resolver with the given shifts and auto map settings
      TDefaultGlslIoResolver defaultResolver(intermediate);
      if (resolver == nullptr) {
          resolver = &defaultResolver;
      }
      resolver->addStage(stage);
      inVarMaps[stage] = new TVarLiveMap, outVarMaps[stage] = new TVarLiveMap(), uniformVarMap[stage] = new TVarLiveMap();
      TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage],
                                           *uniformVarMap[stage]);
      TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage],
                                            *uniformVarMap[stage]);
      root->traverse(&iter_binding_all);
      iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str());
      while (! iter_binding_live.functions.empty()) {
          TIntermNode* function = iter_binding_live.functions.back();
          iter_binding_live.functions.pop_back();
          function->traverse(&iter_binding_live);
      }
      TNotifyInOutAdaptor inOutNotify(stage, *resolver);
      TNotifyUniformAdaptor uniformNotify(stage, *resolver);
      // Resolve current stage input symbol location with previous stage output here,
      // uniform symbol, ubo, ssbo and opaque symbols are per-program resource,
      // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap()
      resolver->beginNotifications(stage);
      std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify);
      std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify);
      std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify);
      resolver->endNotifications(stage);
      TSlotCollector slotCollector(*resolver, infoSink);
      resolver->beginCollect(stage);
      std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector);
      std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector);
      std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector);
      resolver->endCollect(stage);
      intermediates[stage] = &intermediate;
      return !hadError;
  }
  
  bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) {
      resolver->endResolve(EShLangCount);
      if (!hadError) {
          //Resolve uniform location, ubo/ssbo/opaque bindings across stages
          TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError);
          TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError);
          TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError);
          TVarLiveVector uniformVector;
          resolver->beginResolve(EShLangCount);
          for (int stage = EShLangVertex; stage < EShLangCount; stage++) {
              if (inVarMaps[stage] != nullptr) {
                  inOutResolve.setStage(EShLanguage(stage));
                  std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater);
                  std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve);
                  std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater);
                  std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve);
              }
              if (uniformVarMap[stage] != nullptr) {
                  uniformResolve.setStage(EShLanguage(stage));
                  // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info.
                  std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(),
                                [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); });
              }
          }
          std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
              return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
          });
          std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater);
          std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve);
          std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool {
              return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second);
          });
          resolver->endResolve(EShLangCount);
          for (size_t stage = 0; stage < EShLangCount; stage++) {
              if (intermediates[stage] != nullptr) {
                  // traverse each stage, set new location to each input/output and unifom symbol, set new binding to
                  // ubo, ssbo and opaque symbols
                  TVarLiveMap** pUniformVarMap = uniformVarMap;
                  std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) {
                      auto at = pUniformVarMap[stage]->find(p.second.symbol->getName());
                      if (at != pUniformVarMap[stage]->end())
                          at->second = p.second;
                  });
                  TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage],
                                              *uniformVarMap[stage]);
                  intermediates[stage]->getTreeRoot()->traverse(&iter_iomap);
              }
          }
          return !hadError;
      } else {
          return false;
      }
  }
  
  } // end namespace glslang
  
  #endif // GLSLANG_WEB
  