/*******************************************************************************
* Copyright (c) 2012, 2016 Wind River Systems, Inc. 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
*
* Contributors:
* Markus Schorn - initial API and implementation
* Sergey Prigogin (Google)
* Nathan Ridge
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.valueCategoryFromFunctionCall;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.valueCategoryFromReturnType;
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.Arrays;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap;
import org.eclipse.cdt.internal.core.dom.parser.CompositeValue;
import org.eclipse.cdt.internal.core.dom.parser.DependentValue;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecution;
import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext;
import org.eclipse.cdt.internal.core.dom.parser.cpp.OverloadableOperator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics.LookupMode;
import org.eclipse.core.runtime.CoreException;
public final class EvalFunctionCall extends CPPDependentEvaluation {
private final ICPPEvaluation[] fArguments;
private ICPPFunction fOverload = CPPFunction.UNINITIALIZED_FUNCTION;
private IType fType;
private boolean fCheckedIsConstantExpression;
private boolean fIsConstantExpression;
private final ICPPEvaluation fImplicitThis;
public EvalFunctionCall(ICPPEvaluation[] args, ICPPEvaluation owner, IBinding templateDefinition) {
super(templateDefinition);
fArguments = args;
fImplicitThis = getImplicitThis() == owner ? null : owner;
}
public EvalFunctionCall(ICPPEvaluation[] args, ICPPEvaluation owner, IASTNode pointOfDefinition) {
this(args, owner, findEnclosingTemplate(pointOfDefinition));
}
/**
* Returns arguments of the function call. The first argument is the function name, the rest are arguments
* passed to the function.
*/
public ICPPEvaluation[] getArguments() {
return fArguments;
}
private ICPPEvaluation getImplicitThis() {
if (fImplicitThis != null)
return fImplicitThis;
if (fArguments.length > 0) {
if (fArguments[0] instanceof EvalMemberAccess) {
return ((EvalMemberAccess) fArguments[0]).getOwnerEval();
}
}
return null;
}
@Override
public boolean isInitializerList() {
return false;
}
@Override
public boolean isFunctionSet() {
return false;
}
@Override
public boolean isTypeDependent() {
return containsDependentType(fArguments);
}
@Override
public boolean isValueDependent() {
return containsDependentValue(fArguments);
}
@Override
public boolean isConstantExpression(IASTNode point) {
if (!fCheckedIsConstantExpression) {
fCheckedIsConstantExpression = true;
fIsConstantExpression = computeIsConstantExpression(point);
}
return fIsConstantExpression;
}
private boolean computeIsConstantExpression(IASTNode point) {
return areAllConstantExpressions(fArguments, point) && isNullOrConstexprFunc(getOverload(point));
}
public ICPPFunction getOverload(IASTNode point) {
if (fOverload == CPPFunction.UNINITIALIZED_FUNCTION) {
fOverload = computeOverload(point);
}
return fOverload;
}
private ICPPFunction computeOverload(IASTNode point) {
if (isTypeDependent())
return null;
IType t= SemanticUtil.getNestedType(fArguments[0].getType(point), TDEF | REF | CVTYPE);
if (t instanceof ICPPClassType) {
return CPPSemantics.findOverloadedOperator(point, getTemplateDefinitionScope(), fArguments, t,
OverloadableOperator.PAREN, LookupMode.NO_GLOBALS);
}
return null;
}
@Override
public IType getType(IASTNode point) {
if (fType == null)
fType = computeType(point);
return fType;
}
private IType computeType(IASTNode point) {
if (isTypeDependent())
return new TypeOfDependentExpression(this);
ICPPFunction overload = getOverload(point);
if (overload != null)
return ExpressionTypes.typeFromFunctionCall(overload);
ICPPEvaluation function = fArguments[0];
IType result = ExpressionTypes.typeFromFunctionCall(function.getType(point));
if (function instanceof EvalMemberAccess) {
result = ExpressionTypes.restoreTypedefs(result, ((EvalMemberAccess) function).getOwnerType());
}
return result;
}
@Override
public IValue getValue(IASTNode point) {
ICPPEvaluation eval = evaluateFunctionBody(new ConstexprEvaluationContext(point));
if (eval == this) {
return DependentValue.create(eval);
}
return eval.getValue(point);
}
@Override
public ValueCategory getValueCategory(IASTNode point) {
ICPPFunction overload = getOverload(point);
if (overload != null)
return valueCategoryFromFunctionCall(overload);
IType t= fArguments[0].getType(point);
if (t instanceof IPointerType) {
t = SemanticUtil.getNestedType(((IPointerType) t).getType(), TDEF | REF | CVTYPE);
}
if (t instanceof IFunctionType) {
return valueCategoryFromReturnType(((IFunctionType) t).getReturnType());
}
return ValueCategory.PRVALUE;
}
@Override
public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException {
buffer.putShort(ITypeMarshalBuffer.EVAL_FUNCTION_CALL);
buffer.putInt(fArguments.length);
for (ICPPEvaluation arg : fArguments) {
buffer.marshalEvaluation(arg, includeValue);
}
buffer.marshalEvaluation(fImplicitThis, includeValue);
marshalTemplateDefinition(buffer);
}
public static ICPPEvaluation unmarshal(short firstBytes, ITypeMarshalBuffer buffer)
throws CoreException {
int len = buffer.getInt();
ICPPEvaluation[] args = new ICPPEvaluation[len];
for (int i = 0; i < args.length; i++) {
args[i] = buffer.unmarshalEvaluation();
}
ICPPEvaluation implicitThis = buffer.unmarshalEvaluation();
IBinding templateDefinition = buffer.unmarshalBinding();
return new EvalFunctionCall(args, implicitThis, templateDefinition);
}
@Override
public ICPPEvaluation instantiate(InstantiationContext context, int maxDepth) {
ICPPEvaluation[] args = instantiateCommaSeparatedSubexpressions(fArguments, context, maxDepth);
if (args == fArguments)
return this;
if (args[0] instanceof EvalFunctionSet && getOverload(context.getPoint()) == null) {
// Resolve the function using the parameters of the function call.
EvalFunctionSet functionSet = (EvalFunctionSet) args[0];
args[0] = functionSet.resolveFunction(Arrays.copyOfRange(args, 1, args.length), context.getPoint());
}
ICPPEvaluation newImplicitThis = fImplicitThis != null ? fImplicitThis.instantiate(context, maxDepth) : null;
return new EvalFunctionCall(args, newImplicitThis, getTemplateDefinition());
}
@Override
public ICPPEvaluation computeForFunctionCall(ActivationRecord record, ConstexprEvaluationContext context) {
if (context.getStepsPerformed() >= ConstexprEvaluationContext.MAX_CONSTEXPR_EVALUATION_STEPS) {
return EvalFixed.INCOMPLETE;
}
ICPPFunction functionBinding = resolveFunctionBinding(context.getPoint());
if (functionBinding == null)
return EvalFixed.INCOMPLETE;
ICPPEvaluation[] args = new ICPPEvaluation[fArguments.length];
System.arraycopy(fArguments, 0, args, 0, fArguments.length);
ICPPParameter[] parameters = functionBinding.getParameters();
for (int i = 0; i < fArguments.length; i++) {
ICPPEvaluation arg = fArguments[i].computeForFunctionCall(record, context.recordStep());
if (0 < i && i <= parameters.length && isReference(parameters[i - 1]) && fArguments[i] instanceof EvalBinding) {
final EvalBinding evalBinding = (EvalBinding) fArguments[i];
IBinding binding = evalBinding.getBinding();
// If the binding being referenced isn't present in the activation record,
// we won't be able to evaluate the function call.
if (record.getVariable(binding) == null) {
return EvalFixed.INCOMPLETE;
}
arg = new EvalReference(record, binding, evalBinding.getTemplateDefinition());
} else if (0 < i && i <= parameters.length && !isReference(parameters[i - 1])) {
IValue copiedValue = arg.getValue(context.getPoint()).clone();
arg = new EvalFixed(arg.getType(context.getPoint()), arg.getValueCategory(context.getPoint()), copiedValue);
}
args[i] = arg;
}
ICPPEvaluation implicitThis = getImplicitThis();
ICPPEvaluation owner = null;
if (functionBinding instanceof ICPPMethod) {
if (implicitThis instanceof EvalBinding) {
IBinding ownerBinding = ((EvalBinding) implicitThis).getBinding();
if (record.getVariable(ownerBinding) != null) {
owner = new EvalReference(record, ownerBinding, implicitThis.getTemplateDefinition());
} else {
owner = implicitThis;
}
} else if (implicitThis != null) {
owner = implicitThis.computeForFunctionCall(record, context);
} else {
owner = record.getImplicitThis();
}
}
return new EvalFunctionCall(args, owner, getTemplateDefinition()).evaluateFunctionBody(context.recordStep());
}
private ICPPEvaluation evaluateFunctionBody(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, 1, fArguments.length, context.getPoint()))
return EvalFixed.INCOMPLETE;
ICPPFunction function = resolveFunctionBinding(context.getPoint());
if (function == null)
return this;
if (!function.isConstexpr())
return EvalFixed.INCOMPLETE;
ActivationRecord record = createActivationRecord(function.getParameters(), fArguments,
getImplicitThis(), context.getPoint());
ICPPExecution bodyExec = CPPFunction.getFunctionBodyExecution(function, context.getPoint());
if (bodyExec == null) {
if (!(function instanceof ICPPTemplateInstance)
|| ((ICPPTemplateInstance) function).isExplicitSpecialization()) {
return EvalFixed.INCOMPLETE;
}
ICPPTemplateInstance functionInstance = (ICPPTemplateInstance) function;
IBinding specialized = functionInstance.getSpecializedBinding();
if (!(specialized instanceof ICPPFunction))
return this;
bodyExec = CPPFunction.getFunctionBodyExecution((ICPPFunction) specialized, context.getPoint());
}
if (bodyExec != null) {
bodyExec = bodyExec.executeForFunctionCall(record, context.recordStep());
// If the function exited via a return statement, bodyExec.executeForFunctionCall() will have
// just returned the ExecReturn, which needs to be executed separately.
if (bodyExec != null) {
bodyExec = bodyExec.executeForFunctionCall(record, context.recordStep());
if (bodyExec instanceof ExecReturn) {
ExecReturn execReturn = (ExecReturn) bodyExec;
ICPPEvaluation returnValueEval = execReturn.getReturnValueEvaluation();
// TODO(nathanridge): ExecReturn.executeForFunctionCall() already calls
// computeForFunctionCall() on the return value evaluation. Why do we
// need to do it again, and only if it's an EvalBinding?
if (returnValueEval instanceof EvalBinding) {
returnValueEval = returnValueEval.computeForFunctionCall(record, context.recordStep());
}
return returnValueEval;
} else if (bodyExec == ExecIncomplete.INSTANCE) {
return EvalFixed.INCOMPLETE;
}
}
}
return EvalFixed.INCOMPLETE;
}
private ICPPFunction resolveFunctionBinding(IASTNode point) {
ICPPFunction function = getOverload(point);
if (function == null) {
ICPPEvaluation funcEval = fArguments[0];
if (funcEval instanceof EvalFunctionSet) {
EvalFunctionSet funcEvalFunctionSet = (EvalFunctionSet) funcEval;
funcEval = funcEvalFunctionSet.resolveFunction(Arrays.copyOfRange(fArguments, 1, fArguments.length), point);
}
IBinding binding = null;
if (funcEval instanceof EvalBinding) {
EvalBinding funcEvalBinding = (EvalBinding) funcEval;
binding = funcEvalBinding.getBinding();
} else if (funcEval instanceof EvalMemberAccess) {
EvalMemberAccess funcEvalMemberAccess = (EvalMemberAccess) funcEval;
binding = funcEvalMemberAccess.getMember();
}
if (binding instanceof ICPPFunction) {
function = (ICPPFunction) binding;
}
}
return function;
}
private boolean isReference(IBinding binding) {
return binding instanceof IVariable
&& (((IVariable) binding).getType() instanceof ICPPReferenceType || ((IVariable) binding)
.getType() instanceof IPointerType);
}
public static ActivationRecord createActivationRecord(ICPPParameter[] parameters, ICPPEvaluation[] arguments, ICPPEvaluation implicitThis, IASTNode point) {
ActivationRecord record = new ActivationRecord(parameters, implicitThis);
// We start at arguments[1] because arguments[0] is the function's evaluation.
int j = 1;
for (ICPPParameter param : parameters) {
if (param.isParameterPack() || isSpecializedParameterPack(param)) {
// The parameter pack consumes all remaining arguments.
int paramPackLen = arguments.length - j;
ICPPEvaluation[] values = new ICPPEvaluation[paramPackLen];
IType[] types = new IType[paramPackLen];
for (int i = 0; i < paramPackLen; i++) {
ICPPEvaluation arg = arguments[j+i];
values[i] = arg;
types[i] = arg.getType(null);
}
IValue paramPackValue = new CompositeValue(null, values);
IType paramPackType = new ParameterPackType(types);
EvalFixed paramPack = new EvalFixed(paramPackType, ValueCategory.PRVALUE, paramPackValue);
record.update(param, paramPack);
break;
} else {
if (j < arguments.length) {
ICPPEvaluation argument = maybeApplyConversion(arguments[j++], param.getType(), point);
record.update(param, argument);
} else if (param.hasDefaultValue()) {
IValue value = param.getDefaultValue();
ICPPEvaluation eval = value.getEvaluation();
if (eval == null) {
eval = new EvalFixed(param.getType(), ValueCategory.PRVALUE, value);
}
record.update(param, eval);
}
}
}
return record;
}
private static boolean isSpecializedParameterPack(ICPPParameter param) {
if (param instanceof ICPPSpecialization) {
ICPPSpecialization paramSpecialization = (ICPPSpecialization) param;
IBinding specializedBinding = paramSpecialization.getSpecializedBinding();
if (specializedBinding instanceof ICPPParameter) {
ICPPParameter specializedParam = (ICPPParameter) specializedBinding;
return specializedParam.isParameterPack();
}
}
return false;
}
@Override
public int determinePackSize(ICPPTemplateParameterMap tpMap) {
int r = CPPTemplates.PACK_SIZE_NOT_FOUND;
for (ICPPEvaluation arg : fArguments) {
r = CPPTemplates.combinePackSize(r, arg.determinePackSize(tpMap));
}
return r;
}
@Override
public boolean referencesTemplateParameter() {
for (ICPPEvaluation arg : fArguments) {
if (arg.referencesTemplateParameter())
return true;
}
return false;
}
public static class ParameterPackType implements IType {
private final IType[] types;
public ParameterPackType(IType[] types) {
this.types = types;
}
public IType[] getTypes() {
return types;
}
@Override
public boolean isSameType(IType type) {
return false;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
}
return null;
}
}
}