/******************************************************************************* * Copyright (c) 2016 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF; import java.util.HashSet; import java.util.Set; import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory; import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization; import org.eclipse.cdt.core.dom.ast.cpp.ICPPVariable; import org.eclipse.cdt.internal.core.dom.parser.IntegralValue; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation.ConstexprEvaluationContext; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecution; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecutionOwner; public class EvalUtil { /** * The set of ICPPVariable objects for which initial value computation is in progress on each thread. * This is used to guard against recursion during initial value computation. */ private static final ThreadLocal<Set<ICPPVariable>> fInitialValueInProgress = new ThreadLocal<Set<ICPPVariable>>() { @Override protected Set<ICPPVariable> initialValue() { return new HashSet<>(); } }; public static IValue getConditionExprValue(ICPPEvaluation conditionExprEval, ActivationRecord record, ConstexprEvaluationContext context) { return conditionExprEval.computeForFunctionCall(record, context.recordStep()).getValue(context.getPoint()); } public static IValue getConditionDeclValue(ExecSimpleDeclaration conditionDeclExec, ActivationRecord record, ConstexprEvaluationContext context) { ICPPBinding declaredBinding = ((ExecDeclarator) conditionDeclExec.getDeclaratorExecutions()[0]).getDeclaredBinding(); conditionDeclExec.executeForFunctionCall(record, context.recordStep()); return record.getVariable(declaredBinding).computeForFunctionCall(record, context).getValue(context.getPoint()); } public static boolean conditionExprSatisfied(ICPPEvaluation conditionExprEval, ActivationRecord record, ConstexprEvaluationContext context) { Number result = getConditionExprValue(conditionExprEval, record, context).numberValue(); return result != null && result.longValue() != 0; } public static boolean conditionDeclSatisfied(ExecSimpleDeclaration conditionDeclExec, ActivationRecord record, ConstexprEvaluationContext context) { Number result = getConditionDeclValue(conditionDeclExec, record, context).numberValue(); return result != null && result.longValue() != 0; } public static ICPPExecution getExecutionFromStatement(IASTStatement stmt) { if (stmt instanceof ICPPExecutionOwner) { ICPPExecutionOwner execOwner = (ICPPExecutionOwner) stmt; return execOwner.getExecution(); } return null; } // A return value != null means that there was a return, break or continue in that statement. public static ICPPExecution executeStatement(ICPPExecution exec, ActivationRecord record, ConstexprEvaluationContext context) { if (exec instanceof ExecExpressionStatement || exec instanceof ExecDeclarationStatement || exec instanceof ExecCase || exec instanceof ExecDefault) { exec.executeForFunctionCall(record, context.recordStep()); return null; } if (exec instanceof ExecCompoundStatement || exec instanceof ExecWhile || exec instanceof ExecFor || exec instanceof ExecRangeBasedFor || exec instanceof ExecDo || exec instanceof ExecIf || exec instanceof ExecSwitch) { ICPPExecution innerResult = exec.executeForFunctionCall(record, context.recordStep()); if (innerResult instanceof ExecReturn || innerResult instanceof ExecBreak || innerResult instanceof ExecContinue) { return innerResult; } else if (innerResult != null) { return ExecIncomplete.INSTANCE; } return null; } return exec; } private static boolean isUpdateable(ICPPEvaluation eval) { return eval instanceof EvalBinding || (eval instanceof EvalReference && !(eval instanceof EvalPointer)) || eval instanceof EvalCompositeAccess; } /** * Returns a pair of evaluations, each representing the value of 'eval'. * The first, "updateable", is an lvalue (EvalBinding, EvalReference, or EvalCompositeAccess). * The second, "fixed", is a value (usually EvalFixed or EvalPointer). * We return both because, depending on the operation, we may need one representation or another. */ public static Pair<ICPPEvaluation, ICPPEvaluation> getValuePair(ICPPEvaluation eval, ActivationRecord record, ConstexprEvaluationContext context) { ICPPEvaluation updateable = null; if (isUpdateable(eval)) { updateable = eval; } ICPPEvaluation fixed = eval.computeForFunctionCall(record, context.recordStep()); if (isUpdateable(fixed)) { updateable = fixed; if (!(fixed instanceof EvalCompositeAccess)) { fixed = fixed.computeForFunctionCall(record, context); } } return new Pair<ICPPEvaluation, ICPPEvaluation>(updateable, fixed); } public static class Pair<T1, T2> { private final T1 first; private final T2 second; public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public T1 getFirst() { return first; } public T2 getSecond() { return second; } } public static boolean isCompilerGeneratedCtor(IBinding ctor) { if (ctor instanceof ICPPSpecialization) { ICPPSpecialization ctorSpec = (ICPPSpecialization) ctor; return isCompilerGeneratedCtor(ctorSpec.getSpecializedBinding()); } return ctor instanceof ICPPConstructor && ((ICPPConstructor) ctor).isImplicit(); } /** * Returns the initial value of the given variable, evaluated in the context of * the given activation record. */ public static ICPPEvaluation getVariableValue(ICPPVariable variable, ActivationRecord record) { Set<ICPPVariable> recursionProtectionSet = fInitialValueInProgress.get(); if (!recursionProtectionSet.add(variable)) { return EvalFixed.INCOMPLETE; } try { IType type = variable.getType(); IType nestedType = SemanticUtil.getNestedType(type, TDEF | REF | CVTYPE); IValue initialValue = variable.getInitialValue(); ICPPEvaluation valueEval = null; if ((initialValue != null && initialValue.getEvaluation() != null) || (initialValue == null && nestedType instanceof ICPPClassType)) { final ICPPEvaluation initializerEval = initialValue == null ? null : initialValue.getEvaluation(); if (initializerEval == EvalFixed.INCOMPLETE) { return null; } ExecDeclarator declaratorExec = new ExecDeclarator(variable, initializerEval); ConstexprEvaluationContext context = new ConstexprEvaluationContext(null); if (declaratorExec.executeForFunctionCall(record, context) != ExecIncomplete.INSTANCE) { valueEval = record.getVariable(declaratorExec.getDeclaredBinding()); } } else if (initialValue != null) { valueEval = new EvalFixed(type, ValueCategory.LVALUE, initialValue); } if (valueEval != null && (valueEval == EvalFixed.INCOMPLETE || valueEval.getValue(null) == IntegralValue.UNKNOWN)) { return null; } return valueEval; } finally { recursionProtectionSet.remove(variable); } } }