/*******************************************************************************
* 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;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFieldReference;
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.semantics.ActivationRecord;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalInitList;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalUtil;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
public final class CompositeValue implements IValue {
private final ICPPEvaluation evaluation;
private final ICPPEvaluation[] values;
public CompositeValue(ICPPEvaluation evaluation, ICPPEvaluation[] values) {
this.evaluation = evaluation;
for (int i = 0; i < values.length; i++) {
if (values[i] == null)
values[i] = EvalFixed.INCOMPLETE;
}
this.values = values;
}
@Override
public Long numericalValue() {
return null;
}
@Override
public Number numberValue() {
return null;
}
@Override
public ICPPEvaluation getEvaluation() {
return evaluation;
}
@Override
public char[] getSignature() {
if (evaluation != null) {
return evaluation.getSignature();
}
return new char[]{};
}
@Deprecated
@Override
public char[] getInternalExpression() {
return CharArrayUtils.EMPTY_CHAR_ARRAY;
}
@Deprecated
@Override
public IBinding[] getUnknownBindings() {
return IBinding.EMPTY_BINDING_ARRAY;
}
@Override
public int numberOfSubValues() {
return values.length;
}
@Override
public ICPPEvaluation getSubValue(final int index) {
return rangeIsValid(index) ? values[index] : EvalFixed.INCOMPLETE;
}
private boolean rangeIsValid(int index) {
return 0 <= index && index < values.length;
}
public static IValue create(EvalInitList initList) {
ICPPEvaluation[] clauses = initList.getClauses();
ICPPEvaluation[] values = new ICPPEvaluation[clauses.length];
for (int i = 0; i < clauses.length; i++) {
ICPPEvaluation eval = clauses[i];
values[i] = new EvalFixed(eval.getType(null), eval.getValueCategory(null), eval.getValue(null));
}
return new CompositeValue(initList, values);
}
/**
* Creates a value representing an instance of the given array type initialized with
* the elements of the given initializer list.
*/
public static IValue create(EvalInitList initList, IArrayType type, IASTNode point) {
Number arraySize = type.getSize().numberValue();
if (arraySize == null) {
// Array size is dependent. TODO: Handle this?
return IntegralValue.UNKNOWN;
}
// More initializers than array members
if (arraySize.intValue() < initList.getClauses().length) {
return IntegralValue.ERROR;
}
IType elementType = type.getType();
ICPPEvaluation[] values = new ICPPEvaluation[arraySize.intValue()];
for (int i = 0; i < initList.getClauses().length; i++) {
ICPPEvaluation eval = initList.getClauses()[i];
IValue value = getValue(elementType, eval, point);
values[i] = new EvalFixed(elementType, eval.getValueCategory(point), value);
}
return new CompositeValue(initList, values);
}
/**
* Gets the value of an evaluation, interpreted as a value of the given type.
*/
private static IValue getValue(IType type, ICPPEvaluation eval, IASTNode point) {
IValue value;
if (type instanceof IArrayType && eval instanceof EvalInitList) {
value = CompositeValue.create((EvalInitList) eval, (IArrayType) type, point);
} else if (type instanceof ICompositeType && eval instanceof EvalInitList) {
value = CompositeValue.create((EvalInitList) eval, (ICompositeType) type, point);
} else if (eval instanceof EvalInitList) {
value = IntegralValue.UNKNOWN;
} else {
value = eval.getValue(null);
}
return value;
}
/**
* Creates a value representing an instance of the given composite type initialized with
* the elements of the given initializer list.
*/
public static IValue create(EvalInitList initList, ICompositeType type, IASTNode point) {
IField[] fields;
if (type instanceof ICPPClassType) {
fields = ClassTypeHelper.getFields((ICPPClassType) type, point);
} else {
fields = type.getFields();
}
ICPPEvaluation[] values = new ICPPEvaluation[fields.length];
ICPPEvaluation[] clauses = initList.getClauses();
for (int i = 0; i < fields.length; i++) {
if (i == clauses.length)
break;
IField field = fields[i];
ICPPEvaluation eval = clauses[i];
IType fieldType = field.getType();
IValue value = getValue(fieldType, eval, point);
values[i] = new EvalFixed(fieldType, eval.getValueCategory(null), value);
}
return new CompositeValue(initList, values);
}
// The set of class types for which composite value creation is in progress on each thread.
// Used to guard against infinite recursion due to a class (invalidly) aggregating itself.
private static final ThreadLocal<Set<ICPPClassType>> fCreateInProgress =
new ThreadLocal<Set<ICPPClassType>>() {
@Override
protected Set<ICPPClassType> initialValue() {
return new HashSet<>();
}
};
/**
* Creates a value representing an instance of a class type, with the values of the fields
* determined by the default member initializers only. Constructors are not considered
* when determining the values of the fields.
*/
public static CompositeValue create(ICPPClassType classType) {
Set<ICPPClassType> recursionProtectionSet = fCreateInProgress.get();
if (!recursionProtectionSet.add(classType)) {
return new CompositeValue(null, ICPPEvaluation.EMPTY_ARRAY);
}
try {
ActivationRecord record = new ActivationRecord();
ICPPEvaluation[] values = new ICPPEvaluation[ClassTypeHelper.getFields(classType, null).length];
// Recursively create all the base class member variables.
ICPPBase[] bases = ClassTypeHelper.getBases(classType, null);
for (ICPPBase base : bases) {
IBinding baseClass = base.getBaseClass();
if (baseClass instanceof ICPPClassType) {
ICPPClassType baseClassType = (ICPPClassType) baseClass;
ICPPField[] baseFields = ClassTypeHelper.getDeclaredFields(baseClassType, null);
IValue compValue = CompositeValue.create(baseClassType);
for (ICPPField baseField : baseFields) {
int fieldPos = CPPASTFieldReference.getFieldPosition(baseField);
record.update(baseField, compValue.getSubValue(fieldPos));
// TODO(nathanridge): This won't work with multiple inheritance, since 'fieldPos'
// is a field position in the base class' hierarchy, while values[] expects
// as index a field position in classType's hierarchy.
values[fieldPos] = compValue.getSubValue(fieldPos);
}
}
}
ICPPField[] fields = ClassTypeHelper.getDeclaredFields(classType, null);
for (ICPPField field : fields) {
if (field.isStatic())
continue;
final ICPPEvaluation value = EvalUtil.getVariableValue(field, record);
int fieldPos = CPPASTFieldReference.getFieldPosition(field);
record.update(field, value);
values[fieldPos] = value;
}
return new CompositeValue(null, values);
} finally {
recursionProtectionSet.remove(classType);
}
}
@Override
public ICPPEvaluation[] getAllSubValues() {
return values;
}
@Override
public void setSubValue(int position, ICPPEvaluation newValue) {
if (position >= 0 && position < values.length) {
values[position] = newValue == null ? EvalFixed.INCOMPLETE : newValue;
} else {
CCorePlugin.log(IStatus.WARNING, "Out-of-bounds access to composite value: " + position + //$NON-NLS-1$
" (length is " + values.length + ")"); //$NON-NLS-1$//$NON-NLS-2$
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append('[');
for (int i = 0; i < values.length; i++) {
if (i != 0) {
builder.append(',').append(' ');
}
builder.append(values[i].toString());
}
builder.append(']');
return builder.toString();
}
@Override
public IValue clone() {
ICPPEvaluation[] newValues = new ICPPEvaluation[values.length];
for (int i = 0; i < newValues.length; i++) {
ICPPEvaluation eval = values[i];
if (eval == EvalFixed.INCOMPLETE) {
newValues[i] = eval;
} else {
IValue newValue = eval.getValue(null).clone();
newValues[i] = new EvalFixed(eval.getType(null), eval.getValueCategory(null), newValue);
}
}
return new CompositeValue(evaluation, newValues);
}
@Override
public void marshal(ITypeMarshalBuffer buf) throws CoreException {
buf.putShort(ITypeMarshalBuffer.COMPOSITE_VALUE);
buf.marshalEvaluation(evaluation, true);
buf.putInt(values.length);
for (ICPPEvaluation value : values) {
buf.marshalEvaluation(value, true);
}
}
public static IValue unmarshal(short firstBytes, ITypeMarshalBuffer buf) throws CoreException {
ICPPEvaluation evaluation = (ICPPEvaluation) buf.unmarshalEvaluation();
int len = buf.getInt();
ICPPEvaluation values[] = new ICPPEvaluation[len];
for (int i = 0; i < len; i++) {
values[i] = (ICPPEvaluation) buf.unmarshalEvaluation();
}
return new CompositeValue(evaluation, values);
}
}