blob: 0353a1c0974c40b4678931b0961b83660bb04d9f [file] [log] [blame]
/*******************************************************************************
* 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.Map;
import org.eclipse.cdt.core.dom.ast.ASTNodeFactoryFactory;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
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.ICPPASTConstructorInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerList;
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.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
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.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFieldReference;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPDeferredFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPVariable;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
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.core.runtime.CoreException;
public final class EvalConstructor extends CPPDependentEvaluation {
private final IType fType;
private final ICPPConstructor fConstructor;
private final ICPPEvaluation[] fArguments;
private boolean fCheckedIsTypeDependent;
private boolean fIsTypeDependent;
private boolean fCheckedIsConstantExpression;
private boolean fIsConstantExpression;
private static final IASTName TEMP_NAME = ASTNodeFactoryFactory.getDefaultCPPNodeFactory().newName();
public EvalConstructor(IType type, ICPPConstructor constructor, ICPPEvaluation[] arguments,
IBinding templateDefinition) {
super(templateDefinition);
fType = type;
fConstructor = constructor;
fArguments = arguments != null ? arguments : ICPPEvaluation.EMPTY_ARRAY;
}
public EvalConstructor(IType type, ICPPConstructor constructor, ICPPEvaluation[] arguments,
IASTNode pointOfDefinition) {
this(type, constructor, arguments, findEnclosingTemplate(pointOfDefinition));
}
@Override
public boolean isInitializerList() {
return false;
}
@Override
public boolean isFunctionSet() {
return false;
}
@Override
public boolean isTypeDependent() {
if (!fCheckedIsTypeDependent) {
fCheckedIsTypeDependent = true;
fIsTypeDependent = CPPTemplates.isDependentType(fType) || containsDependentType(fArguments);
}
return fIsTypeDependent;
}
@Override
public boolean isValueDependent() {
if (CPPTemplates.isDependentType(fType))
return true;
for (ICPPEvaluation arg : fArguments) {
if (arg.isValueDependent())
return true;
}
return false;
}
@Override
public boolean isConstantExpression() {
if (!fCheckedIsConstantExpression) {
fCheckedIsConstantExpression = true;
fIsConstantExpression = computeIsConstantExpression();
}
return fIsConstantExpression;
}
private boolean computeIsConstantExpression() {
return fConstructor.isConstexpr() && areAllConstantExpressions(fArguments);
}
@Override
public boolean isEquivalentTo(ICPPEvaluation other) {
if (!(other instanceof EvalConstructor)) {
return false;
}
EvalConstructor o = (EvalConstructor) other;
return fType.isSameType(o.fType)
&& fConstructor == o.fConstructor
&& areEquivalentEvaluations(fArguments, o.fArguments);
}
@Override
public IType getType() {
return fType;
}
public ICPPConstructor getConstructor() {
return fConstructor;
}
public ICPPEvaluation[] getArguments() {
return fArguments;
}
@Override
public IValue getValue() {
ICPPEvaluation computed =
computeForFunctionCall(new ActivationRecord(), new ConstexprEvaluationContext());
if (computed == this)
return IntegralValue.ERROR;
return computed.getValue();
}
@Override
public ValueCategory getValueCategory() {
return null;
}
@Override
public ICPPEvaluation computeForFunctionCall(ActivationRecord callSiteRecord,
ConstexprEvaluationContext context) {
final IType unwrappedType = SemanticUtil.getNestedType(fType, TDEF | REF | CVTYPE);
if (!(unwrappedType instanceof ICPPClassType)) {
return this;
}
final ICPPClassType classType = (ICPPClassType) unwrappedType;
final CompositeValue compositeValue = CompositeValue.create(classType);
ICPPEvaluation[] argList = evaluateArguments(fArguments, callSiteRecord, context);
EvalFixed constructedObject = new EvalFixed(fType, ValueCategory.PRVALUE, compositeValue);
CPPVariable binding = new CPPVariable(TEMP_NAME);
ActivationRecord localRecord = EvalFunctionCall.createActivationRecord(fConstructor.getParameters(),
argList, constructedObject);
localRecord.update(binding, constructedObject);
ICPPExecution exec = fConstructor.getConstructorChainExecution();
if (exec instanceof ExecConstructorChain) {
ExecConstructorChain memberInitList = (ExecConstructorChain) exec;
Map<IBinding, ICPPEvaluation> ccInitializers = memberInitList.getConstructorChainInitializers();
for (Map.Entry<IBinding, ICPPEvaluation> ccInitializer : ccInitializers.entrySet()) {
if (ccInitializer.getKey() instanceof ICPPConstructor) {
ICPPClassType baseClassType = (ICPPClassType) ccInitializer.getKey().getOwner();
final ICPPEvaluation memberEval = ccInitializer.getValue();
ICPPEvaluation memberValue =
memberEval.computeForFunctionCall(localRecord, context.recordStep());
ICPPEvaluation[] baseClassValues = memberValue.getValue().getAllSubValues();
ICPPField[] baseFields = ClassTypeHelper.getFields(baseClassType);
for (ICPPField baseField : baseFields) {
// TODO: This has the same problem with multiple inheritance as
// CompositeValue.create(ICPPClassType).
int fieldPos = CPPASTFieldReference.getFieldPosition(baseField);
constructedObject.getValue().setSubValue(fieldPos, baseClassValues[fieldPos]);
}
}
}
}
ICPPField[] fields = classType.getDeclaredFields();
for (ICPPField field : fields) {
if (field.isStatic()) {
continue;
}
final Map.Entry<IBinding, ICPPEvaluation> initializer =
getInitializerFromMemberInitializerList(field, exec);
ICPPEvaluation value = null;
if (initializer != null) {
ExecDeclarator declaratorExec = getDeclaratorExecutionFromMemberInitializerList(initializer);
value = getFieldValue(declaratorExec, classType, localRecord, context);
} else {
value = EvalUtil.getVariableValue(field, localRecord);
}
final int fieldPos = CPPASTFieldReference.getFieldPosition(field);
compositeValue.setSubValue(fieldPos, value);
}
// TODO(nathanridge): EvalFunctionCall.computeForFunctionCall() will:
// - evaluate the arguments again
// - create another ActivationRecord (inside evaluateFunctionBody())
// Are these necessary?
new EvalFunctionCall(argList, constructedObject, CPPSemantics.getCurrentLookupPoint())
.computeForFunctionCall(localRecord, context.recordStep());
return localRecord.getVariable(binding);
}
private Map.Entry<IBinding, ICPPEvaluation> getInitializerFromMemberInitializerList(ICPPField field,
ICPPExecution exec) {
if (!(exec instanceof ExecConstructorChain)) {
return null;
}
final ExecConstructorChain initList = (ExecConstructorChain) exec;
for (Map.Entry<IBinding, ICPPEvaluation> init : initList.getConstructorChainInitializers().entrySet()) {
final IBinding member = init.getKey();
if (member instanceof ICPPField && member.getName().equals(field.getName())) {
return init;
}
}
return null;
}
private ExecDeclarator getDeclaratorExecutionFromMemberInitializerList(
Map.Entry<IBinding, ICPPEvaluation> ccInitializer) {
final ICPPBinding member = (ICPPBinding) ccInitializer.getKey();
final ICPPEvaluation memberEval = ccInitializer.getValue();
return new ExecDeclarator(member, memberEval);
}
private ICPPEvaluation getFieldValue(ExecDeclarator declaratorExec, ICPPClassType classType,
ActivationRecord record, ConstexprEvaluationContext context) {
if (declaratorExec == null) {
return null;
}
if (declaratorExec.executeForFunctionCall(record, context) != ExecIncomplete.INSTANCE) {
final ICPPEvaluation value = record.getVariable(declaratorExec.getDeclaredBinding());
return value;
}
return null;
}
public static ICPPEvaluation[] extractArguments(IASTInitializer initializer, ICPPConstructor constructor) {
ICPPEvaluation[] args = extractArguments(initializer);
if (args.length == 1 && constructor.getParameters().length > 1 && args[0] instanceof EvalInitList) {
EvalInitList evalInitList = (EvalInitList) args[0];
args = evalInitList.getClauses();
}
return args;
}
public static ICPPEvaluation[] extractArguments(IASTInitializer initializer) {
if (initializer == null) {
return ICPPEvaluation.EMPTY_ARRAY;
} else if (initializer instanceof ICPPASTConstructorInitializer) {
ICPPASTConstructorInitializer ctorInitializer = (ICPPASTConstructorInitializer) initializer;
return evaluateArguments(ctorInitializer.getArguments());
} else if (initializer instanceof ICPPASTInitializerList) {
ICPPASTInitializerList initList = (ICPPASTInitializerList) initializer;
return evaluateArguments(initList.getClauses());
} else if (initializer instanceof IASTEqualsInitializer) {
IASTEqualsInitializer equalsInitalizer = (IASTEqualsInitializer) initializer;
IASTInitializerClause initClause = equalsInitalizer.getInitializerClause();
return evaluateArguments(initClause);
} else {
throw new IllegalArgumentException(initializer.getClass().getSimpleName()
+ " type of initializer is not supported"); //$NON-NLS-1$
}
}
private static ICPPEvaluation[] evaluateArguments(IASTInitializerClause... clauses) {
ICPPEvaluation[] args = new ICPPEvaluation[clauses.length];
for (int i = 0; i < clauses.length; i++) {
ICPPASTInitializerClause clause = (ICPPASTInitializerClause) clauses[i];
args[i] = clause.getEvaluation();
}
return args;
}
private ICPPEvaluation[] evaluateArguments(ICPPEvaluation[] arguments, ActivationRecord record,
ConstexprEvaluationContext context) {
ICPPEvaluation[] argList = new ICPPEvaluation[arguments.length + 1];
EvalBinding constructorBinding =
new EvalBinding(fConstructor, fConstructor.getType(), getTemplateDefinition());
argList[0] = constructorBinding;
for (int i = 0; i < arguments.length; i++) {
ICPPEvaluation evaluatedClause = arguments[i].computeForFunctionCall(record, context.recordStep());
argList[i+1] = evaluatedClause;
}
return argList;
}
@Override
public int determinePackSize(ICPPTemplateParameterMap tpMap) {
int r = CPPTemplates.determinePackSize(fType, tpMap);
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;
}
@Override
public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException {
buffer.putShort(ITypeMarshalBuffer.EVAL_CONSTRUCTOR);
buffer.marshalType(fType);
buffer.marshalBinding(fConstructor);
buffer.putInt(fArguments.length);
for (ICPPEvaluation arg : fArguments) {
buffer.marshalEvaluation(arg, includeValue);
}
marshalTemplateDefinition(buffer);
}
public static ICPPEvaluation unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException {
IType type = buffer.unmarshalType();
ICPPConstructor constructor = (ICPPConstructor) buffer.unmarshalBinding();
int len = buffer.getInt();
ICPPEvaluation[] arguments = new ICPPEvaluation[len];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = buffer.unmarshalEvaluation();
}
IBinding templateDefinition = buffer.unmarshalBinding();
return new EvalConstructor(type, constructor, arguments, templateDefinition);
}
@Override
public ICPPEvaluation instantiate(InstantiationContext context, int maxDepth) {
ICPPEvaluation[] newArguments = new ICPPEvaluation[fArguments.length];
for (int i = 0; i < fArguments.length; i++) {
newArguments[i] = fArguments[i].instantiate(context, maxDepth);
}
IType newType = null;
ICPPConstructor newConstructor;
try {
newConstructor = (ICPPConstructor) CPPTemplates.instantiateBinding(fConstructor, context, maxDepth);
if (newConstructor != null) {
newType = newConstructor.getClassOwner();
}
if (newConstructor instanceof CPPDeferredFunction) {
ICPPFunction[] candidates = ((CPPDeferredFunction) newConstructor).getCandidates();
if (candidates != null) {
CPPFunctionSet functionSet =
new CPPFunctionSet(candidates, new ICPPTemplateArgument[]{}, null);
EvalFunctionSet evalFunctionSet =
new EvalFunctionSet(functionSet, false, false, newType,
CPPSemantics.getCurrentLookupPoint());
ICPPEvaluation resolved = evalFunctionSet.resolveFunction(newArguments);
if (resolved instanceof EvalBinding) {
EvalBinding evalBinding = (EvalBinding) resolved;
newConstructor = (ICPPConstructor) evalBinding.getBinding();
if (newConstructor != null) {
newType = newConstructor.getClassOwner();
}
}
}
}
} catch (DOMException e) {
newConstructor = fConstructor;
}
// Only instantiate fType separately if we couldn't get the instantiated type
// via newConstructor.getClassOwner() for some reason. This is not just for
// efficiency; instantiating fType directly will not work if fType is a
// CPPClassTemplate because CPPTemplates.instantiateType() does not instantiate
// CPPClassTemplates, only CPPDeferredClassInstances (TODO: why?).
if (newType == null) {
newType = CPPTemplates.instantiateType(fType, context);
}
return new EvalConstructor(newType, newConstructor, newArguments, getTemplateDefinition());
}
}