/******************************************************************************* * Copyright (c) 2008, 2012 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) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_alignof; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_nothrow_constructor; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_nothrow_copy; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_assign; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_constructor; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_copy; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_trivial_destructor; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_has_virtual_destructor; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_abstract; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_class; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_empty; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_enum; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_literal_type; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_pod; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_polymorphic; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_standard_layout; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_trivial; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_is_union; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_sizeof; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_typeid; import static org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression.op_typeof; import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTBinaryTypeIdExpression; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; import org.eclipse.cdt.core.dom.ast.IEnumeration; import org.eclipse.cdt.core.dom.ast.IEnumerator; 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.ICPPASTInitializerClause; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateNonTypeParameter; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment; 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.ICPPUnknownBinding; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalBinding; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.TypeTraits; import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator; import org.eclipse.cdt.internal.core.parser.scanner.ExpressionEvaluator.EvalException; import org.eclipse.cdt.internal.core.pdom.db.TypeMarshalBuffer; import org.eclipse.core.runtime.CoreException; /** * Represents values of variables, enumerators or expressions. The primary purpose of * the representation is to support instantiation of templates with non-type template parameters. */ public class Value implements IValue { public static final int MAX_RECURSION_DEPTH = 25; public static final Value UNKNOWN= new Value("".toCharArray(), null); //$NON-NLS-1$ public static final Value NOT_INITIALIZED= new Value("<__>".toCharArray(), null); //$NON-NLS-1$ private static final char UNIQUE_CHAR = '_'; private final static IValue[] TYPICAL= { new Value(new char[] {'0'}, null), new Value(new char[] {'1'}, null), new Value(new char[] {'2'}, null), new Value(new char[] {'3'}, null), new Value(new char[] {'4'}, null), new Value(new char[] {'5'}, null), new Value(new char[] {'6'}, null)}; private static class UnknownValueException extends Exception {} private static UnknownValueException UNKNOWN_EX= new UnknownValueException(); private static int sUnique= 0; // The following invariant always holds: (fFixedValue == null) != (fEvaluation == null) private final char[] fFixedValue; private final ICPPEvaluation fEvaluation; private char[] fSignature; private Value(char[] fixedValue, ICPPEvaluation evaluation) { assert (fixedValue == null) != (evaluation == null); fFixedValue = fixedValue; fEvaluation = evaluation; } @Override public Long numericalValue() { return fFixedValue == null ? null : parseLong(fFixedValue); } @Override public ICPPEvaluation getEvaluation() { return fEvaluation; } @Override public char[] getSignature() { if (fSignature == null) { fSignature = fFixedValue != null ? fFixedValue : fEvaluation.getSignature(); } return fSignature; } @Deprecated @Override public char[] getInternalExpression() { return CharArrayUtils.EMPTY_CHAR_ARRAY; } @Deprecated @Override public IBinding[] getUnknownBindings() { return IBinding.EMPTY_BINDING_ARRAY; } public void marshall(ITypeMarshalBuffer buf) throws CoreException { if (UNKNOWN == this) { buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG1)); } else { Long num= numericalValue(); if (num != null) { long lv= num; if (lv >= Integer.MIN_VALUE && lv <= Integer.MAX_VALUE) { buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG2)); buf.putInt((int) lv); } else { buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG3)); buf.putLong(lv); } } else if (fFixedValue != null) { buf.putByte((byte) (ITypeMarshalBuffer.VALUE | ITypeMarshalBuffer.FLAG4)); buf.putCharArray(fFixedValue); } else { buf.putByte((ITypeMarshalBuffer.VALUE)); fEvaluation.marshal(buf, true); } } } public static IValue unmarshal(ITypeMarshalBuffer buf) throws CoreException { int firstByte= buf.getByte(); if (firstByte == TypeMarshalBuffer.NULL_TYPE) return null; if ((firstByte & ITypeMarshalBuffer.FLAG1) != 0) return Value.UNKNOWN; if ((firstByte & ITypeMarshalBuffer.FLAG2) != 0) { int val= buf.getInt(); return Value.create(val); } if ((firstByte & ITypeMarshalBuffer.FLAG3) != 0) { long val= buf.getLong(); return Value.create(val); } if ((firstByte & ITypeMarshalBuffer.FLAG4) != 0) { char[] fixedValue = buf.getCharArray(); return new Value(fixedValue, null); } ISerializableEvaluation eval= buf.unmarshalEvaluation(); if (eval instanceof ICPPEvaluation) return new Value(null, (ICPPEvaluation) eval); return Value.UNKNOWN; } @Override public int hashCode() { return CharArrayUtils.hash(getSignature()); } @Override public boolean equals(Object obj) { if (!(obj instanceof Value)) { return false; } final Value rhs = (Value) obj; if (fFixedValue != null) return CharArrayUtils.equals(fFixedValue, rhs.fFixedValue); return CharArrayUtils.equals(getSignature(), rhs.getSignature()); } @Override public String toString() { return new String(getSignature()); } /** * Creates a value representing the given number. */ public static IValue create(long value) { if (value >=0 && value < TYPICAL.length) return TYPICAL[(int) value]; return new Value(toCharArray(value), null); } /** * Creates a value object representing the given boolean value. */ public static IValue create(boolean value) { return create(value ? 1 : 0); } /** * Creates a value representing the given template parameter. */ public static IValue create(ICPPTemplateNonTypeParameter tntp) { EvalBinding eval = new EvalBinding(tntp, null); return new Value(null, eval); } /** * Create a value wrapping the given evaluation. */ public static IValue create(ICPPEvaluation eval) { return new Value(null, eval); } public static IValue evaluateBinaryExpression(final int op, final long v1, final long v2) { try { return create(applyBinaryOperator(op, v1, v2)); } catch (UnknownValueException e) { } return UNKNOWN; } public static IValue evaluateUnaryExpression(final int unaryOp, final long value) { try { return create(applyUnaryOperator(unaryOp, value)); } catch (UnknownValueException e) { } return UNKNOWN; } public static IValue evaluateUnaryTypeIdExpression(int operator, IType type, IASTNode point) { try { return create(applyUnaryTypeIdOperator(operator, type, point)); } catch (UnknownValueException e) { } return UNKNOWN; } public static IValue evaluateBinaryTypeIdExpression(IASTBinaryTypeIdExpression.Operator operator, IType type1, IType type2, IASTNode point) { try { return create(applyBinaryTypeIdOperator(operator, type1, type2, point)); } catch (UnknownValueException e) { } return UNKNOWN; } private static long applyUnaryTypeIdOperator(int operator, IType type, IASTNode point) throws UnknownValueException{ switch (operator) { case op_sizeof: return getSizeAndAlignment(type, point).size; case op_alignof: return getSizeAndAlignment(type, point).alignment; case op_typeid: break; // TODO(sprigogin): Implement case op_has_nothrow_copy: break; // TODO(sprigogin): Implement case op_has_nothrow_constructor: break; // TODO(sprigogin): Implement case op_has_trivial_assign: break; // TODO(sprigogin): Implement case op_has_trivial_constructor: break; // TODO(sprigogin): Implement case op_has_trivial_copy: return !(type instanceof ICPPClassType) || TypeTraits.hasTrivialCopyCtor((ICPPClassType) type, point) ? 1 : 0; case op_has_trivial_destructor: break; // TODO(sprigogin): Implement case op_has_virtual_destructor: break; // TODO(sprigogin): Implement case op_is_abstract: return type instanceof ICPPClassType && TypeTraits.isAbstract((ICPPClassType) type, point) ? 1 : 0; case op_is_class: return type instanceof ICompositeType && ((ICompositeType) type).getKey() != ICompositeType.k_union ? 1 : 0; case op_is_empty: break; // TODO(sprigogin): Implement case op_is_enum: return type instanceof IEnumeration ? 1 : 0; case op_is_literal_type: break; // TODO(sprigogin): Implement case op_is_pod: return TypeTraits.isPOD(type, point) ? 1 : 0; case op_is_polymorphic: return type instanceof ICPPClassType && TypeTraits.isPolymorphic((ICPPClassType) type, point) ? 1 : 0; case op_is_standard_layout: return TypeTraits.isStandardLayout(type, point) ? 1 : 0; case op_is_trivial: return type instanceof ICPPClassType && TypeTraits.isTrivial((ICPPClassType) type, point) ? 1 : 0; case op_is_union: return type instanceof ICompositeType && ((ICompositeType) type).getKey() == ICompositeType.k_union ? 1 : 0; case op_typeof: break; // TODO(sprigogin): Implement } throw UNKNOWN_EX; } public static long applyBinaryTypeIdOperator(IASTBinaryTypeIdExpression.Operator operator, IType type1, IType type2, IASTNode point) throws UnknownValueException { switch (operator) { case __is_base_of: if (type1 instanceof ICPPClassType && type1 instanceof ICPPClassType) { return ClassTypeHelper.isSubclass((ICPPClassType) type2, (ICPPClassType) type1) ? 1 : 0; } } throw UNKNOWN_EX; } private static SizeAndAlignment getSizeAndAlignment(IType type, IASTNode point) throws UnknownValueException { SizeAndAlignment sizeAndAlignment = SizeofCalculator.getSizeAndAlignment(type, point); if (sizeAndAlignment == null) throw UNKNOWN_EX; return sizeAndAlignment; } /** * Tests whether the value is a template parameter (or a parameter pack). * * @return the parameter id of the parameter, or -1 if it is not a template * parameter. */ public static int isTemplateParameter(IValue tval) { ICPPEvaluation eval = tval.getEvaluation(); if (eval instanceof EvalBinding) { IBinding binding = ((EvalBinding) eval).getBinding(); if (binding instanceof ICPPTemplateParameter) { return ((ICPPTemplateParameter) binding).getParameterID(); } } return -1; } /** * Tests whether the value references some template parameter. */ public static boolean referencesTemplateParameter(IValue tval) { ICPPEvaluation eval = tval.getEvaluation(); if (eval == null) return false; return eval.referencesTemplateParameter(); } /** * Tests whether the value depends on a template parameter. */ public static boolean isDependentValue(IValue nonTypeValue) { return nonTypeValue.getEvaluation() != null; } /** * Creates the value for an expression. */ public static IValue create(IASTExpression expr, int maxRecursionDepth) { try { Object obj= evaluate(expr, maxRecursionDepth); if (obj instanceof Long) return create(((Long) obj).longValue()); if (expr instanceof ICPPASTInitializerClause) { ICPPEvaluation evaluation = ((ICPPASTInitializerClause) expr).getEvaluation(); return new Value(null, evaluation); } } catch (UnknownValueException e) { } return UNKNOWN; } /** * Creates a value off its canonical representation. */ public static IValue fromInternalRepresentation(ICPPEvaluation evaluation) { return new Value(null, evaluation); } /** * Creates a unique value needed during template instantiation. */ public static IValue unique() { StringBuilder buf= new StringBuilder(10); buf.append(UNIQUE_CHAR); buf.append(++sUnique); return new Value(CharArrayUtils.extractChars(buf), null); } /** * Computes the canonical representation of the value of the expression. * Returns a {@code Long} for numerical values or {@code null}, otherwise. * @throws UnknownValueException */ private static Long evaluate(IASTExpression exp, int maxdepth) throws UnknownValueException { if (maxdepth < 0 || exp == null) throw UNKNOWN_EX; if (exp instanceof IASTArraySubscriptExpression) { throw UNKNOWN_EX; } if (exp instanceof IASTBinaryExpression) { return evaluateBinaryExpression((IASTBinaryExpression) exp, maxdepth); } if (exp instanceof IASTCastExpression) { // must be ahead of unary return evaluate(((IASTCastExpression) exp).getOperand(), maxdepth); } if (exp instanceof IASTUnaryExpression) { return evaluateUnaryExpression((IASTUnaryExpression) exp, maxdepth); } if (exp instanceof IASTConditionalExpression) { IASTConditionalExpression cexpr= (IASTConditionalExpression) exp; Long v= evaluate(cexpr.getLogicalConditionExpression(), maxdepth); if (v == null) return null; if (v.longValue() == 0) { return evaluate(cexpr.getNegativeResultExpression(), maxdepth); } final IASTExpression pe = cexpr.getPositiveResultExpression(); if (pe == null) // gnu-extension allows to omit the positive expression. return v; return evaluate(pe, maxdepth); } if (exp instanceof IASTIdExpression) { IBinding b= ((IASTIdExpression) exp).getName().resolvePreBinding(); return evaluateBinding(b, maxdepth); } if (exp instanceof IASTLiteralExpression) { IASTLiteralExpression litEx= (IASTLiteralExpression) exp; switch (litEx.getKind()) { case IASTLiteralExpression.lk_false: case IASTLiteralExpression.lk_nullptr: return Long.valueOf(0); case IASTLiteralExpression.lk_true: return Long.valueOf(1); case IASTLiteralExpression.lk_integer_constant: try { return ExpressionEvaluator.getNumber(litEx.getValue()); } catch (EvalException e) { throw UNKNOWN_EX; } case IASTLiteralExpression.lk_char_constant: try { final char[] image= litEx.getValue(); if (image.length > 1 && image[0] == 'L') return ExpressionEvaluator.getChar(image, 2); return ExpressionEvaluator.getChar(image, 1); } catch (EvalException e) { throw UNKNOWN_EX; } } } if (exp instanceof IASTTypeIdExpression) { ASTTranslationUnit ast = (ASTTranslationUnit) exp.getTranslationUnit(); final IType type = ast.createType(((IASTTypeIdExpression) exp).getTypeId()); if (type instanceof ICPPUnknownType) return null; return applyUnaryTypeIdOperator(((IASTTypeIdExpression) exp).getOperator(), type, exp); } if (exp instanceof IASTBinaryTypeIdExpression) { } throw UNKNOWN_EX; } /** * Extract a value off a binding. */ private static Long evaluateBinding(IBinding b, int maxdepth) throws UnknownValueException { if (b instanceof IType) { throw UNKNOWN_EX; } if (b instanceof ICPPTemplateNonTypeParameter) { return null; } if (b instanceof ICPPUnknownBinding) { return null; } IValue value= null; if (b instanceof IInternalVariable) { value= ((IInternalVariable) b).getInitialValue(maxdepth - 1); } else if (b instanceof IVariable) { value= ((IVariable) b).getInitialValue(); } else if (b instanceof IEnumerator) { value= ((IEnumerator) b).getValue(); } if (value != null && value != Value.UNKNOWN) { return value.numericalValue(); } throw UNKNOWN_EX; } private static Long evaluateUnaryExpression(IASTUnaryExpression exp, int maxdepth) throws UnknownValueException { final int unaryOp= exp.getOperator(); if (unaryOp == IASTUnaryExpression.op_sizeof) { final IASTExpression operand = exp.getOperand(); if (operand != null) { IType type = operand.getExpressionType(); if (type instanceof ICPPUnknownType) return null; ASTTranslationUnit ast = (ASTTranslationUnit) exp.getTranslationUnit(); SizeofCalculator calculator = ast.getSizeofCalculator(); SizeAndAlignment info = calculator.sizeAndAlignment(type); if (info != null) return info.size; } throw UNKNOWN_EX; } if (unaryOp == IASTUnaryExpression.op_amper || unaryOp == IASTUnaryExpression.op_star || unaryOp == IASTUnaryExpression.op_sizeofParameterPack) { throw UNKNOWN_EX; } final Long value= evaluate(exp.getOperand(), maxdepth); if (value == null) return null; return applyUnaryOperator(unaryOp, value); } private static long applyUnaryOperator(final int unaryOp, final long value) throws UnknownValueException { switch (unaryOp) { case IASTUnaryExpression.op_bracketedPrimary: case IASTUnaryExpression.op_plus: return value; } switch (unaryOp) { case IASTUnaryExpression.op_prefixIncr: case IASTUnaryExpression.op_postFixIncr: return value + 1; case IASTUnaryExpression.op_prefixDecr: case IASTUnaryExpression.op_postFixDecr: return value - 1; case IASTUnaryExpression.op_minus: return -value; case IASTUnaryExpression.op_tilde: return ~value; case IASTUnaryExpression.op_not: return value == 0 ? 1 : 0; } throw UNKNOWN_EX; } private static Long evaluateBinaryExpression(IASTBinaryExpression exp, int maxdepth) throws UnknownValueException { final int op= exp.getOperator(); switch (op) { case IASTBinaryExpression.op_equals: if (exp.getOperand1().equals(exp.getOperand2())) return Long.valueOf(1); break; case IASTBinaryExpression.op_notequals: if (exp.getOperand1().equals(exp.getOperand2())) return Long.valueOf(0); break; } final Long o1= evaluate(exp.getOperand1(), maxdepth); if (o1 == null) return null; final Long o2= evaluate(exp.getOperand2(), maxdepth); if (o2 == null) return null; return applyBinaryOperator(op, o1, o2); } private static long applyBinaryOperator(final int op, final long v1, final long v2) throws UnknownValueException { switch (op) { case IASTBinaryExpression.op_multiply: return v1 * v2; case IASTBinaryExpression.op_divide: if (v2 == 0) throw UNKNOWN_EX; return v1 / v2; case IASTBinaryExpression.op_modulo: if (v2 == 0) throw UNKNOWN_EX; return v1 % v2; case IASTBinaryExpression.op_plus: return v1 + v2; case IASTBinaryExpression.op_minus: return v1 - v2; case IASTBinaryExpression.op_shiftLeft: return v1 << v2; case IASTBinaryExpression.op_shiftRight: return v1 >> v2; case IASTBinaryExpression.op_lessThan: return v1 < v2 ? 1 : 0; case IASTBinaryExpression.op_greaterThan: return v1 > v2 ? 1 : 0; case IASTBinaryExpression.op_lessEqual: return v1 <= v2 ? 1 : 0; case IASTBinaryExpression.op_greaterEqual: return v1 >= v2 ? 1 : 0; case IASTBinaryExpression.op_binaryAnd: return v1 & v2; case IASTBinaryExpression.op_binaryXor: return v1 ^ v2; case IASTBinaryExpression.op_binaryOr: return v1 | v2; case IASTBinaryExpression.op_logicalAnd: return v1 != 0 && v2 != 0 ? 1 : 0; case IASTBinaryExpression.op_logicalOr: return v1 != 0 || v2 != 0 ? 1 : 0; case IASTBinaryExpression.op_equals: return v1 == v2 ? 1 : 0; case IASTBinaryExpression.op_notequals: return v1 != v2 ? 1 : 0; case IASTBinaryExpression.op_max: return Math.max(v1, v2); case IASTBinaryExpression.op_min: return Math.min(v1, v2); } throw UNKNOWN_EX; } /** * Parses a long, returns null if not possible */ private static Long parseLong(char[] value) { final long maxvalue= Long.MAX_VALUE / 10; final int len= value.length; boolean negative= false; long result = 0; int i= 0; if (len > 0 && value[0] == '-') { negative = true; i++; } if (i == len) return null; for (; i < len; i++) { if (result > maxvalue) return null; final int digit= (value[i] - '0'); if (digit < 0 || digit > 9) return null; result= result * 10 + digit; } return negative ? -result : result; } /** * Converts long to a char array */ private static char[] toCharArray(long value) { StringBuilder buf= new StringBuilder(); buf.append(value); return CharArrayUtils.extractChars(buf); } }