blob: 60df52c7151a896a18bf6cbd20b3f6345958966f [file] [log] [blame]
//
// 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;
}