text: //
  // Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  //
  
  #include "compiler/DetectCallDepth.h"
  #include "compiler/InfoSink.h"
  
  DetectCallDepth::FunctionNode::FunctionNode(const TString& fname)
      : name(fname),
        visit(PreVisit)
  {
  }
  
  const TString& DetectCallDepth::FunctionNode::getName() const
  {
      return name;
  }
  
  void DetectCallDepth::FunctionNode::addCallee(
      DetectCallDepth::FunctionNode* callee)
  {
      for (size_t i = 0; i < callees.size(); ++i) {
          if (callees[i] == callee)
              return;
      }
      callees.push_back(callee);
  }
  
  int DetectCallDepth::FunctionNode::detectCallDepth(DetectCallDepth* detectCallDepth, int depth)
  {
      ASSERT(visit == PreVisit);
      ASSERT(detectCallDepth);
  
      int maxDepth = depth;
      visit = InVisit;
      for (size_t i = 0; i < callees.size(); ++i) {
          switch (callees[i]->visit) {
              case InVisit:
                  // cycle detected, i.e., recursion detected.
                  return kInfiniteCallDepth;
              case PostVisit:
                  break;
              case PreVisit: {
                  // Check before we recurse so we don't go too depth
                  if (detectCallDepth->checkExceedsMaxDepth(depth))
                      return depth;
                  int callDepth = callees[i]->detectCallDepth(detectCallDepth, depth + 1);
                  // Check after we recurse so we can exit immediately and provide info.
                  if (detectCallDepth->checkExceedsMaxDepth(callDepth)) {
                      detectCallDepth->getInfoSink().info << "<-" << callees[i]->getName();
                      return callDepth;
                  }
                  maxDepth = std::max(callDepth, maxDepth);
                  break;
              }
              default:
                  UNREACHABLE();
                  break;
          }
      }
      visit = PostVisit;
      return maxDepth;
  }
  
  void DetectCallDepth::FunctionNode::reset()
  {
      visit = PreVisit;
  }
  
  DetectCallDepth::DetectCallDepth(TInfoSink& infoSink, bool limitCallStackDepth, int maxCallStackDepth)
      : TIntermTraverser(true, false, true, false),
        currentFunction(NULL),
        infoSink(infoSink),
        maxDepth(limitCallStackDepth ? maxCallStackDepth : FunctionNode::kInfiniteCallDepth)
  {
  }
  
  DetectCallDepth::~DetectCallDepth()
  {
      for (size_t i = 0; i < functions.size(); ++i)
          delete functions[i];
  }
  
  bool DetectCallDepth::visitAggregate(Visit visit, TIntermAggregate* node)
  {
      switch (node->getOp())
      {
          case EOpPrototype:
              // Function declaration.
              // Don't add FunctionNode here because node->getName() is the
              // unmangled function name.
              break;
          case EOpFunction: {
              // Function definition.
              if (visit == PreVisit) {
                  currentFunction = findFunctionByName(node->getName());
                  if (currentFunction == NULL) {
                      currentFunction = new FunctionNode(node->getName());
                      functions.push_back(currentFunction);
                  }
              } else if (visit == PostVisit) {
                  currentFunction = NULL;
              }
              break;
          }
          case EOpFunctionCall: {
              // Function call.
              if (visit == PreVisit) {
                  FunctionNode* func = findFunctionByName(node->getName());
                  if (func == NULL) {
                      func = new FunctionNode(node->getName());
                      functions.push_back(func);
                  }
                  if (currentFunction)
                      currentFunction->addCallee(func);
              }
              break;
          }
          default:
              break;
      }
      return true;
  }
  
  bool DetectCallDepth::checkExceedsMaxDepth(int depth)
  {
      return depth >= maxDepth;
  }
  
  void DetectCallDepth::resetFunctionNodes()
  {
      for (size_t i = 0; i < functions.size(); ++i) {
          functions[i]->reset();
      }
  }
  
  DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepthForFunction(FunctionNode* func)
  {
      currentFunction = NULL;
      resetFunctionNodes();
  
      int maxCallDepth = func->detectCallDepth(this, 1);
  
      if (maxCallDepth == FunctionNode::kInfiniteCallDepth)
          return kErrorRecursion;
  
      if (maxCallDepth >= maxDepth)
          return kErrorMaxDepthExceeded;
  
      return kErrorNone;
  }
  
  DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepth()
  {
      if (maxDepth != FunctionNode::kInfiniteCallDepth) {
          // Check all functions because the driver may fail on them
          // TODO: Before detectingRecursion, strip unused functions.
          for (size_t i = 0; i < functions.size(); ++i) {
              ErrorCode error = detectCallDepthForFunction(functions[i]);
              if (error != kErrorNone)
                  return error;
          }
      } else {
          FunctionNode* main = findFunctionByName("main(");
          if (main == NULL)
              return kErrorMissingMain;
  
          return detectCallDepthForFunction(main);
      }
  
      return kErrorNone;
  }
  
  DetectCallDepth::FunctionNode* DetectCallDepth::findFunctionByName(
      const TString& name)
  {
      for (size_t i = 0; i < functions.size(); ++i) {
          if (functions[i]->getName() == name)
              return functions[i];
      }
      return NULL;
  }
  
  