1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 22:22:11 +02:00

Bug 429891 - Limit the total number of computation steps in a constexpr

evaluation

Change-Id: I057d94a78e92d26c9ad824b2ea0c6a4ad896e948
Signed-off-by: Nathan Ridge <zeratul976@hotmail.com>
Reviewed-on: https://git.eclipse.org/r/24925
Tested-by: Hudson CI
Reviewed-by: Sergey Prigogin <eclipse.sprigogin@gmail.com>
Tested-by: Sergey Prigogin <eclipse.sprigogin@gmail.com>
This commit is contained in:
Nathan Ridge 2014-04-13 19:37:01 -04:00 committed by Sergey Prigogin
parent ec10b77e67
commit d66e5c04bf
18 changed files with 110 additions and 46 deletions

View file

@ -8490,4 +8490,22 @@ public class AST2TemplateTests extends AST2TestBase {
IVariable waldo = helper.assertNonProblem("waldo");
assertEquals(5, waldo.getInitialValue().numericalValue().longValue());
}
// constexpr int naive_fibonacci(int x) {
// return x == 0 ? 0
// : x == 1 ? 1
// : naive_fibonacci(x - 2) + naive_fibonacci(x - 1);
// }
//
// constexpr int waldo = naive_fibonacci(50);
public void testConstexprEvaluationLimit_429891() throws Exception {
// Here we're just checking that the computation of the initial
// value finishes (with a null result) in a reasonable time.
// If we tried to run the computation of naive_fibonacci(50)
// to its end, the IDE would appear to hang.
BindingAssertionHelper helper = getAssertionHelper();
IVariable waldo = helper.assertNonProblem("waldo");
assertNull(waldo.getInitialValue().numericalValue());
}
}

View file

@ -85,16 +85,62 @@ public interface ICPPEvaluation extends ISerializableEvaluation {
ICPPEvaluation instantiate(ICPPTemplateParameterMap tpMap, int packOffset,
ICPPClassSpecialization within, int maxdepth, IASTNode point);
/**
* Keeps track of state during a constexpr evaluation.
*/
class ConstexprEvaluationContext {
/**
* The maximum number of steps allowed in a single constexpr evaluation.
* This is used to prevent a buggy constexpr function from causing the
* IDE to hang.
*/
public static final int MAX_CONSTEXPR_EVALUATION_STEPS = 1024;
private int fStepsPerformed;
private IASTNode fPoint;
/**
* Construct a ConstexprEvaluationContext for a new constexpr evaluation.
* @param point the point of instantiation, determines the scope for name lookups
*/
public ConstexprEvaluationContext(IASTNode point) {
fStepsPerformed = 0;
fPoint = point;
}
/**
* Record a new step being performed in this constexpr evaluation.
* @return this constexpr evaluation
*/
public ConstexprEvaluationContext recordStep() {
++fStepsPerformed;
return this;
}
/**
* Get the number of steps performed so far in the constexpr evaluation.
*/
public int getStepsPerformed() {
return fStepsPerformed;
}
/**
* Get the point of instantiation.
*/
public IASTNode getPoint() {
return fPoint;
}
}
/**
* Computes the evaluation produced by substituting function parameters by their values.
*
* @param parameterMap maps function parameters to their values
* @param maxdepth allowed recursion depth
* @param point the point of instantiation, determines the scope for name lookups
* @param context the context for the current constexpr evaluation
* @return the computed evaluation
*/
ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
IASTNode point);
ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
ConstexprEvaluationContext context);
/**
* Searches the evaluation for a usage of a template parameter which is a parameter pack,

View file

@ -366,9 +366,9 @@ public class EvalBinary extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ICPPEvaluation arg1 = fArg1.computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation arg2 = fArg2.computeForFunctionCall(parameterMap, maxdepth, point);
ConstexprEvaluationContext context) {
ICPPEvaluation arg1 = fArg1.computeForFunctionCall(parameterMap, context.recordStep());
ICPPEvaluation arg2 = fArg2.computeForFunctionCall(parameterMap, context.recordStep());
if (arg1 == fArg1 && arg2 == fArg2)
return this;
return new EvalBinary(fOperator, arg1, arg2, getTemplateDefinition());

View file

@ -142,7 +142,7 @@ public class EvalBinaryTypeId extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
return this;
}

View file

@ -405,7 +405,7 @@ public class EvalBinding extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
int pos = getFunctionParameterPosition();
if (pos >= 0) {
ICPPEvaluation eval = parameterMap.getArgument(pos);

View file

@ -204,10 +204,10 @@ public class EvalComma extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
ICPPEvaluation[] args = fArguments;
for (int i = 0; i < fArguments.length; i++) {
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context.recordStep());
if (arg != fArguments[i]) {
if (args == fArguments) {
args = new ICPPEvaluation[fArguments.length];

View file

@ -109,8 +109,8 @@ public class EvalCompound extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ICPPEvaluation delegate = fDelegate.computeForFunctionCall(parameterMap, maxdepth, point);
ConstexprEvaluationContext context) {
ICPPEvaluation delegate = fDelegate.computeForFunctionCall(parameterMap, context.recordStep());
if (delegate == fDelegate)
return this;
return new EvalCompound(delegate, getTemplateDefinition());

View file

@ -354,23 +354,23 @@ public class EvalConditional extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ICPPEvaluation condition = fCondition.computeForFunctionCall(parameterMap, maxdepth, point);
ConstexprEvaluationContext context) {
ICPPEvaluation condition = fCondition.computeForFunctionCall(parameterMap, context.recordStep());
// If the condition can be evaluated, fold the conditional into
// just the branch that is taken. This avoids infinite recursion
// when computing a recursive constexpr function where the base
// case of the recursion is one of the branches of the conditional.
Long conditionValue = condition.getValue(point).numericalValue();
Long conditionValue = condition.getValue(context.getPoint()).numericalValue();
if (conditionValue != null) {
if (conditionValue.longValue() != 0) {
return fPositive == null ? null : fPositive.computeForFunctionCall(parameterMap, maxdepth, point);
return fPositive == null ? null : fPositive.computeForFunctionCall(parameterMap, context.recordStep());
} else {
return fNegative.computeForFunctionCall(parameterMap, maxdepth, point);
return fNegative.computeForFunctionCall(parameterMap, context.recordStep());
}
}
ICPPEvaluation positive = fPositive == null ?
null : fPositive.computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation negative = fNegative.computeForFunctionCall(parameterMap, maxdepth, point);
null : fPositive.computeForFunctionCall(parameterMap, context.recordStep());
ICPPEvaluation negative = fNegative.computeForFunctionCall(parameterMap, context.recordStep());
if (condition == fCondition && positive == fPositive && negative == fNegative)
return this;
return new EvalConditional(condition, positive, negative, fPositiveThrows, fNegativeThrows, getTemplateDefinition());

View file

@ -175,11 +175,11 @@ public class EvalFixed extends CPPEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
ICPPEvaluation eval = fValue.getEvaluation();
if (eval == null)
return this;
eval = eval.computeForFunctionCall(parameterMap, maxdepth, point);
eval = eval.computeForFunctionCall(parameterMap, context.recordStep());
if (eval == fValue.getEvaluation())
return this;
return new EvalFixed(fType, fValueCategory, Value.create(eval));

View file

@ -145,7 +145,7 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
@Override
public IValue getValue(IASTNode point) {
ICPPEvaluation eval = computeForFunctionCall(Value.MAX_RECURSION_DEPTH, point);
ICPPEvaluation eval = computeForFunctionCall(new ConstexprEvaluationContext(point));
if (eval == this) {
return Value.create(eval);
}
@ -205,13 +205,13 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
if (maxdepth == 0)
ConstexprEvaluationContext context) {
if (context.getStepsPerformed() >= ConstexprEvaluationContext.MAX_CONSTEXPR_EVALUATION_STEPS)
return EvalFixed.INCOMPLETE;
ICPPEvaluation[] args = fArguments;
for (int i = 0; i < fArguments.length; i++) {
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context);
if (arg != fArguments[i]) {
if (args == fArguments) {
args = new ICPPEvaluation[fArguments.length];
@ -223,17 +223,17 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
EvalFunctionCall eval = this;
if (args != fArguments)
eval = new EvalFunctionCall(args, getTemplateDefinition());
return eval.computeForFunctionCall(maxdepth - 1, point);
return eval.computeForFunctionCall(context);
}
private ICPPEvaluation computeForFunctionCall(int maxdepth, IASTNode point) {
private ICPPEvaluation computeForFunctionCall(ConstexprEvaluationContext context) {
if (isValueDependent())
return this;
// If the arguments are not all constant expressions, there is
// no point trying to substitute them into the return expression.
if (!areAllConstantExpressions(fArguments, point))
if (!areAllConstantExpressions(fArguments, context.getPoint()))
return this;
ICPPFunction function = getOverload(point);
ICPPFunction function = getOverload(context.getPoint());
if (function == null) {
if (fArguments[0] instanceof EvalBinding) {
IBinding binding = ((EvalBinding) fArguments[0]).getBinding();
@ -247,7 +247,7 @@ public class EvalFunctionCall extends CPPDependentEvaluation {
if (eval == null)
return EvalFixed.INCOMPLETE;
CPPFunctionParameterMap parameterMap = buildParameterMap(function);
return eval.computeForFunctionCall(parameterMap, maxdepth, point);
return eval.computeForFunctionCall(parameterMap, context.recordStep());
}
private CPPFunctionParameterMap buildParameterMap(ICPPFunction function) {

View file

@ -266,7 +266,7 @@ public class EvalFunctionSet extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
return this;
}

View file

@ -362,10 +362,10 @@ public class EvalID extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
if (fFieldOwner == null)
return this;
ICPPEvaluation fieldOwner = fFieldOwner.computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation fieldOwner = fFieldOwner.computeForFunctionCall(parameterMap, context.recordStep());
if (fieldOwner == fFieldOwner)
return this;
return new EvalID(fieldOwner, fNameOwner, fName, fAddressOf, fQualified, fTemplateArgs, getTemplateDefinition());

View file

@ -119,10 +119,10 @@ public class EvalInitList extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
ICPPEvaluation[] clauses = fClauses;
for (int i = 0; i < fClauses.length; i++) {
ICPPEvaluation clause = fClauses[i].computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation clause = fClauses[i].computeForFunctionCall(parameterMap, context.recordStep());
if (clause != fClauses[i]) {
if (clauses == fClauses) {
clauses = new ICPPEvaluation[fClauses.length];

View file

@ -355,7 +355,7 @@ public class EvalMemberAccess extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
return this;
}

View file

@ -105,8 +105,8 @@ public class EvalParameterPack extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ICPPEvaluation expansionPattern = fExpansionPattern.computeForFunctionCall(parameterMap, maxdepth, point);
ConstexprEvaluationContext context) {
ICPPEvaluation expansionPattern = fExpansionPattern.computeForFunctionCall(parameterMap, context.recordStep());
if (expansionPattern == fExpansionPattern)
return this;
return new EvalParameterPack(expansionPattern, getTemplateDefinition());

View file

@ -239,11 +239,11 @@ public class EvalTypeId extends CPPDependentEvaluation {
}
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
IASTNode point) {
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
ConstexprEvaluationContext context) {
ICPPEvaluation[] args = fArguments;
for (int i = 0; i < fArguments.length; i++) {
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, maxdepth, point);
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(parameterMap, context.recordStep());
if (arg != fArguments[i]) {
if (args == fArguments) {
args = new ICPPEvaluation[fArguments.length];

View file

@ -345,9 +345,9 @@ public class EvalUnary extends CPPDependentEvaluation {
}
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap, int maxdepth,
IASTNode point) {
ICPPEvaluation argument = fArgument.computeForFunctionCall(parameterMap, maxdepth, point);
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
ConstexprEvaluationContext context) {
ICPPEvaluation argument = fArgument.computeForFunctionCall(parameterMap, context.recordStep());
if (argument == fArgument)
return this;

View file

@ -220,7 +220,7 @@ public class EvalUnaryTypeID extends CPPDependentEvaluation {
@Override
public ICPPEvaluation computeForFunctionCall(CPPFunctionParameterMap parameterMap,
int maxdepth, IASTNode point) {
ConstexprEvaluationContext context) {
return this;
}