+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.ast;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.impl.Constant;
+import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
-/**
- * An expression.
- * @author Matthieu Casanova
- */
-public abstract class Expression extends Statement {
-
- /**
- * Create an expression giving starting and ending offset
- * @param sourceStart starting offset
- * @param sourceEnd ending offset
- */
- protected Expression(final int sourceStart, final int sourceEnd) {
- super(sourceStart, sourceEnd);
- }
-
- /**
- * Return the expression with a number of spaces before.
- * @param tab how many spaces before the expression
- * @return a string representing the expression
- */
- public final String toString(final int tab) {
- return tabString(tab) + toStringExpression();
- }
-
- /**
- * Return the expression as String.
- * @return the expression
- */
- public abstract String toStringExpression();
+public class Expression extends Statement {
+
+ //some expression may not be used - from a java semantic point
+ //of view only - as statements. Other may. In order to avoid the creation
+ //of wrappers around expression in order to tune them as expression
+ //Expression is a subclass of Statement. See the message isValidJavaStatement()
+
+ public int implicitConversion;
+ public TypeBinding resolvedType;
+
+ public Constant constant;
+
+ public Expression() {
+ super();
+ }
+
+ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
+
+ return flowInfo;
+ }
+
+ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
+
+ return analyseCode(currentScope, flowContext, flowInfo);
+ }
+
+ /**
+ * Constant usable for bytecode pattern optimizations, but cannot be inlined
+ * since it is not strictly equivalent to the definition of constant expressions.
+ * In particular, some side-effects may be required to occur (only the end value
+ * is known).
+ * Constant is known to be of boolean type
+ */
+ public Constant optimizedBooleanConstant() {
+
+ return this.constant;
+ }
+
+ public static final boolean isConstantValueRepresentable(
+ Constant constant,
+ int constantTypeID,
+ int targetTypeID) {
+
+ //true if there is no loss of precision while casting.
+ // constantTypeID == constant.typeID
+ if (targetTypeID == constantTypeID)
+ return true;
+ switch (targetTypeID) {
+ case T_char :
+ switch (constantTypeID) {
+ case T_char :
+ return true;
+ case T_double :
+ return constant.doubleValue() == constant.charValue();
+ case T_float :
+ return constant.floatValue() == constant.charValue();
+ case T_int :
+ return constant.intValue() == constant.charValue();
+ case T_short :
+ return constant.shortValue() == constant.charValue();
+ case T_byte :
+ return constant.byteValue() == constant.charValue();
+ case T_long :
+ return constant.longValue() == constant.charValue();
+ default :
+ return false;//boolean
+ }
+
+ case T_float :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.floatValue();
+ case T_double :
+ return constant.doubleValue() == constant.floatValue();
+ case T_float :
+ return true;
+ case T_int :
+ return constant.intValue() == constant.floatValue();
+ case T_short :
+ return constant.shortValue() == constant.floatValue();
+ case T_byte :
+ return constant.byteValue() == constant.floatValue();
+ case T_long :
+ return constant.longValue() == constant.floatValue();
+ default :
+ return false;//boolean
+ }
+
+ case T_double :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.doubleValue();
+ case T_double :
+ return true;
+ case T_float :
+ return constant.floatValue() == constant.doubleValue();
+ case T_int :
+ return constant.intValue() == constant.doubleValue();
+ case T_short :
+ return constant.shortValue() == constant.doubleValue();
+ case T_byte :
+ return constant.byteValue() == constant.doubleValue();
+ case T_long :
+ return constant.longValue() == constant.doubleValue();
+ default :
+ return false; //boolean
+ }
+
+ case T_byte :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.byteValue();
+ case T_double :
+ return constant.doubleValue() == constant.byteValue();
+ case T_float :
+ return constant.floatValue() == constant.byteValue();
+ case T_int :
+ return constant.intValue() == constant.byteValue();
+ case T_short :
+ return constant.shortValue() == constant.byteValue();
+ case T_byte :
+ return true;
+ case T_long :
+ return constant.longValue() == constant.byteValue();
+ default :
+ return false; //boolean
+ }
+
+ case T_short :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.shortValue();
+ case T_double :
+ return constant.doubleValue() == constant.shortValue();
+ case T_float :
+ return constant.floatValue() == constant.shortValue();
+ case T_int :
+ return constant.intValue() == constant.shortValue();
+ case T_short :
+ return true;
+ case T_byte :
+ return constant.byteValue() == constant.shortValue();
+ case T_long :
+ return constant.longValue() == constant.shortValue();
+ default :
+ return false; //boolean
+ }
+
+ case T_int :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.intValue();
+ case T_double :
+ return constant.doubleValue() == constant.intValue();
+ case T_float :
+ return constant.floatValue() == constant.intValue();
+ case T_int :
+ return true;
+ case T_short :
+ return constant.shortValue() == constant.intValue();
+ case T_byte :
+ return constant.byteValue() == constant.intValue();
+ case T_long :
+ return constant.longValue() == constant.intValue();
+ default :
+ return false; //boolean
+ }
+
+ case T_long :
+ switch (constantTypeID) {
+ case T_char :
+ return constant.charValue() == constant.longValue();
+ case T_double :
+ return constant.doubleValue() == constant.longValue();
+ case T_float :
+ return constant.floatValue() == constant.longValue();
+ case T_int :
+ return constant.intValue() == constant.longValue();
+ case T_short :
+ return constant.shortValue() == constant.longValue();
+ case T_byte :
+ return constant.byteValue() == constant.longValue();
+ case T_long :
+ return true;
+ default :
+ return false; //boolean
+ }
+
+ default :
+ return false; //boolean
+ }
+ }
+
+ /**
+ * Expression statements are plain expressions, however they generate like
+ * normal expressions with no value required.
+ *
+ * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
+ * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
+ */
+// public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+//
+// if ((bits & IsReachableMASK) == 0) {
+// return;
+// }
+// generateCode(currentScope, codeStream, false);
+// }
+
+ /**
+ * Every expression is responsible for generating its implicit conversion when necessary.
+ *
+ * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
+ * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
+ * @param valueRequired boolean
+ */
+// public void generateCode(
+// BlockScope currentScope,
+// CodeStream codeStream,
+// boolean valueRequired) {
+//
+// if (constant != NotAConstant) {
+// // generate a constant expression
+// int pc = codeStream.position;
+// codeStream.generateConstant(constant, implicitConversion);
+// codeStream.recordPositionsFrom(pc, this.sourceStart);
+// } else {
+// // actual non-constant code generation
+// throw new ShouldNotImplement(ProjectPrefUtil.bind("ast.missingCode")); //$NON-NLS-1$
+// }
+// }
+
+ /**
+ * Default generation of a boolean value
+ */
+// public void generateOptimizedBoolean(
+// BlockScope currentScope,
+// CodeStream codeStream,
+// Label trueLabel,
+// Label falseLabel,
+// boolean valueRequired) {
+//
+// // a label valued to nil means: by default we fall through the case...
+// // both nil means we leave the value on the stack
+//
+// if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean)) {
+// int pc = codeStream.position;
+// if (constant.booleanValue() == true) {
+// // constant == true
+// if (valueRequired) {
+// if (falseLabel == null) {
+// // implicit falling through the FALSE case
+// if (trueLabel != null) {
+// codeStream.goto_(trueLabel);
+// }
+// }
+// }
+// } else {
+// if (valueRequired) {
+// if (falseLabel != null) {
+// // implicit falling through the TRUE case
+// if (trueLabel == null) {
+// codeStream.goto_(falseLabel);
+// }
+// }
+// }
+// }
+// codeStream.recordPositionsFrom(pc, this.sourceStart);
+// return;
+// }
+// generateCode(currentScope, codeStream, valueRequired);
+// // branching
+// int position = codeStream.position;
+// if (valueRequired) {
+// if (falseLabel == null) {
+// if (trueLabel != null) {
+// // Implicit falling through the FALSE case
+// codeStream.ifne(trueLabel);
+// }
+// } else {
+// if (trueLabel == null) {
+// // Implicit falling through the TRUE case
+// codeStream.ifeq(falseLabel);
+// } else {
+// // No implicit fall through TRUE/FALSE --> should never occur
+// }
+// }
+// }
+// // reposition the endPC
+// codeStream.updateLastRecordedEndPC(position);
+// }
+//
+// /* Optimized (java) code generation for string concatenations that involve StringBuffer
+// * creation: going through this path means that there is no need for a new StringBuffer
+// * creation, further operands should rather be only appended to the current one.
+// * By default: no optimization.
+// */
+// public void generateOptimizedStringBuffer(
+// BlockScope blockScope,
+// net.sourceforge.phpdt.internal.compiler.codegen.CodeStream codeStream,
+// int typeID) {
+//
+// generateCode(blockScope, codeStream, true);
+// codeStream.invokeStringBufferAppendForType(typeID);
+// }
+
+ /* Optimized (java) code generation for string concatenations that involve StringBuffer
+ * creation: going through this path means that there is no need for a new StringBuffer
+ * creation, further operands should rather be only appended to the current one.
+ */
+// public void generateOptimizedStringBufferCreation(
+// BlockScope blockScope,
+// CodeStream codeStream,
+// int typeID) {
+//
+// // Optimization only for integers and strings
+// if (typeID == T_Object) {
+// // in the case the runtime value of valueOf(Object) returns null, we have to use append(Object) instead of directly valueOf(Object)
+// // append(Object) returns append(valueOf(Object)), which means that the null case is handled by append(String).
+// codeStream.newStringBuffer();
+// codeStream.dup();
+// codeStream.invokeStringBufferDefaultConstructor();
+// generateCode(blockScope, codeStream, true);
+// codeStream.invokeStringBufferAppendForType(T_Object);
+// return;
+// }
+// codeStream.newStringBuffer();
+// codeStream.dup();
+// if (typeID == T_String || typeID == T_null) {
+// if (constant != NotAConstant) {
+// codeStream.ldc(constant.stringValue());
+// } else {
+// generateCode(blockScope, codeStream, true);
+// codeStream.invokeStringValueOf(T_Object);
+// }
+// } else {
+// generateCode(blockScope, codeStream, true);
+// codeStream.invokeStringValueOf(typeID);
+// }
+// codeStream.invokeStringBufferStringConstructor();
+// }
+
+ // Base types need that the widening is explicitly done by the compiler using some bytecode like i2f
+ public void implicitWidening(
+ TypeBinding runtimeTimeType,
+ TypeBinding compileTimeType) {
+
+ if (runtimeTimeType == null || compileTimeType == null)
+ return;
+
+// if (compileTimeType.id == T_null) {
+// // this case is possible only for constant null
+// // The type of runtime is a reference type
+// // The code gen use the constant id thus any value
+// // for the runtime id (akak the <<4) could be used.
+// // T_Object is used as some general T_reference
+// implicitConversion = (T_Object << 4) + T_null;
+// return;
+// }
+
+ switch (runtimeTimeType.id) {
+ case T_byte :
+ case T_short :
+ case T_char :
+ implicitConversion = (T_int << 4) + compileTimeType.id;
+ break;
+ case T_String :
+ case T_float :
+ case T_boolean :
+ case T_double :
+ case T_int : //implicitConversion may result in i2i which will result in NO code gen
+ case T_long :
+ implicitConversion = (runtimeTimeType.id << 4) + compileTimeType.id;
+ break;
+ default : //nothing on regular object ref
+ }
+ }
+
+ public boolean isCompactableOperation() {
+
+ return false;
+ }
+
+ //Return true if the conversion is done AUTOMATICALLY by the vm
+ //while the javaVM is an int based-machine, thus for example pushing
+ //a byte onto the stack , will automatically creates a int on the stack
+ //(this request some work d be done by the VM on signed numbers)
+ public boolean isConstantValueOfTypeAssignableToType(
+ TypeBinding constantType,
+ TypeBinding targetType) {
+
+ if (constant == Constant.NotAConstant)
+ return false;
+ if (constantType == targetType)
+ return true;
+ if (constantType.isBaseType() && targetType.isBaseType()) {
+ //No free assignment conversion from anything but to integral ones.
+ if ((constantType == IntBinding
+ || BaseTypeBinding.isWidening(T_int, constantType.id))
+ && (BaseTypeBinding.isNarrowing(targetType.id, T_int))) {
+ //use current explicit conversion in order to get some new value to compare with current one
+ return isConstantValueRepresentable(constant, constantType.id, targetType.id);
+ }
+ }
+ return false;
+ }
+
+ public boolean isTypeReference() {
+ return false;
+ }
+ public StringBuffer print(int indent, StringBuffer output) {
+ printIndent(indent, output);
+ return printExpression(indent, output);
+ }
+
+ public StringBuffer printExpression(int indent, StringBuffer output) {
+ output.append(super.toString(0));
+ return output;
+ }
+
+ public StringBuffer printStatement(int indent, StringBuffer output) {
+ return print(indent, output).append(";"); //$NON-NLS-1$
+ }
+ public void resolve(BlockScope scope) {
+ // drops the returning expression's type whatever the type is.
+
+ this.resolveType(scope);
+ return;
+ }
+
+ public TypeBinding resolveType(BlockScope scope) {
+ // by default... subclasses should implement a better TC if required.
+
+ return null;
+ }
+
+ public TypeBinding resolveTypeExpecting(
+ BlockScope scope,
+ TypeBinding expectedType) {
+
+ TypeBinding expressionType = this.resolveType(scope);
+ if (expressionType == null) return null;
+ if (expressionType == expectedType) return expressionType;
+
+ if (!expressionType.isCompatibleWith(expectedType)) {
+ scope.problemReporter().typeMismatchError(expressionType, expectedType, this);
+ return null;
+ }
+ return expressionType;
+ }
+
+ public String toString(int tab) {
+
+ //Subclass re-define toStringExpression
+ String s = tabString(tab);
+ if (constant != null)
+ //before TC has runned
+ if (constant != NotAConstant)
+ //after the TC has runned
+ s += " /*cst:" + constant.toString() + "*/ "; //$NON-NLS-1$ //$NON-NLS-2$
+ return s + toStringExpression(tab);
+ }
+
+ //Subclass re-define toStringExpression
+ //This method is abstract and should never be called
+ //but we provide some code that is running.....just in case
+ //of developpement time (while every thing is not built)
+ public String toStringExpression() {
+
+ return super.toString(0);
+ }
+
+ public String toStringExpression(int tab) {
+ // default is regular toString expression (qualified allocation expressions redifine this method)
+ return this.toStringExpression();
+ }
+
+ public Expression toTypeReference() {
+ //by default undefined
+
+ //this method is meanly used by the parser in order to transform
+ //an expression that is used as a type reference in a cast ....
+ //--appreciate the fact that castExpression and ExpressionWithParenthesis
+ //--starts with the same pattern.....
+
+ return this;
+ }
}