blob: 8595400d8ce3ffb6795dff17cf5e01e69f1f46a1 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.ast;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.IAbstractSyntaxTreeVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.problem.*;
import org.eclipse.jdt.internal.compiler.util.Util;
public abstract 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 Constant constant;
public Expression() {
super();
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
return analyseCode(currentScope, flowContext, flowInfo);
}
public Constant conditionalConstant(){
return constant;
}
/* Dislike this name
*/
public static final boolean convertToTypeFromTypeValue(int left , int right, Constant cst){
//true if there is no loose of information while casting.
//right is constant's type.id
//a faster solution would be to use combinaison of
//some range.........????
/*
org.eclipse.jdt.internal.compiler.util.Constant cst ;
cst = org.eclipse.jdt.internal.compiler.util.Constant.fromValue((float)898565456.0) ;
org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding.convertToTypeFromTypeValue(
org.eclipse.jdt.internal.compiler.ast.TypeReference.T_int,
org.eclipse.jdt.internal.compiler.ast.TypeReference.T_float,
cst)
*/
if (left == right) return true ;
switch(left){
case T_char :
switch (right) {
case T_char : return true ;
case T_double : return cst.doubleValue() == cst.charValue() ;
case T_float : return cst.floatValue() == cst.charValue() ;
case T_int : return cst.intValue() == cst.charValue() ;
case T_short : return cst.shortValue() == cst.charValue() ;
case T_byte : return cst.byteValue() == cst.charValue() ;
case T_long : return cst.longValue() == cst.charValue() ;
default : return false ;} //boolean
case T_float :
switch (right) {
case T_char : return cst.charValue() == cst.floatValue() ;
case T_double : return cst.doubleValue() == cst.floatValue() ;
case T_float : return true;
case T_int : return cst.intValue() == cst.floatValue() ;
case T_short : return cst.shortValue() == cst.floatValue() ;
case T_byte : return cst.byteValue() == cst.floatValue() ;
case T_long : return cst.longValue() == cst.floatValue() ;
default : return false ;} //boolean
case T_double :
switch (right) {
case T_char : return cst.charValue() == cst.doubleValue() ;
case T_double : return true ;
case T_float : return cst.floatValue() == cst.doubleValue() ;
case T_int : return cst.intValue() == cst.doubleValue() ;
case T_short : return cst.shortValue() == cst.doubleValue() ;
case T_byte : return cst.byteValue() == cst.doubleValue() ;
case T_long : return cst.longValue() == cst.doubleValue() ;
default : return false ;} //boolean
case T_byte :
switch (right) {
case T_char : return cst.charValue() == cst.byteValue() ;
case T_double : return cst.doubleValue() == cst.byteValue() ;
case T_float : return cst.floatValue() == cst.byteValue() ;
case T_int : return cst.intValue() == cst.byteValue() ;
case T_short : return cst.shortValue() == cst.byteValue() ;
case T_byte : return true ;
case T_long : return cst.longValue() == cst.byteValue() ;
default : return false ;} //boolean
case T_short :
switch (right) {
case T_char : return cst.charValue() == cst.shortValue() ;
case T_double : return cst.doubleValue() == cst.shortValue() ;
case T_float : return cst.floatValue() == cst.shortValue() ;
case T_int : return cst.intValue() == cst.shortValue() ;
case T_short : return true ;
case T_byte : return cst.byteValue() == cst.shortValue() ;
case T_long : return cst.longValue() == cst.shortValue() ;
default : return false ;} //boolean
case T_int :
switch (right) {
case T_char : return cst.charValue() == cst.intValue() ;
case T_double : return cst.doubleValue() == cst.intValue() ;
case T_float : return cst.floatValue() == cst.intValue() ;
case T_int : return true ;
case T_short : return cst.shortValue() == cst.intValue() ;
case T_byte : return cst.byteValue() == cst.intValue() ;
case T_long : return cst.longValue() == cst.intValue() ;
default : return false ;} //boolean
case T_long :
switch (right) {
case T_char : return cst.charValue() == cst.longValue() ;
case T_double : return cst.doubleValue() == cst.longValue() ;
case T_float : return cst.floatValue() == cst.longValue() ;
case T_int : return cst.intValue() == cst.longValue() ;
case T_short : return cst.shortValue() == cst.longValue() ;
case T_byte : return cst.byteValue() == cst.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 org.eclipse.jdt.internal.compiler.lookup.BlockScope
* @param codeStream org.eclipse.jdt.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 org.eclipse.jdt.internal.compiler.lookup.BlockScope
* @param codeStream org.eclipse.jdt.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);
} else {
// actual non-constant code generation
throw new ShouldNotImplement(Util.bind("ast.missingCode"/*nonNLS*/));
}
}
/**
* 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);
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);
}
public void generateOptimizedStringBuffer(BlockScope blockScope, org.eclipse.jdt.internal.compiler.codegen.CodeStream codeStream, int 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.
* By default: no optimization.
*/
generateCode(blockScope, codeStream, true);
codeStream.invokeStringBufferAppendForType(typeID);
}
public void generateOptimizedStringBufferCreation(BlockScope blockScope, CodeStream codeStream, int 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.
*/
// 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();
}
public void implicitWidening(TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
// Base types need that the widening is explicitly done by the compiler using some bytecode like i2f
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;
}
public boolean isConstantValueOfTypeAssignableToType(TypeBinding constantType, TypeBinding targetType) {
//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)
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 convertToTypeFromTypeValue(targetType.id, constantType.id, constant);
}
}
return false;
}
public boolean isTypeReference() {
return false;
}
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 expectedTb) {
TypeBinding thisTb = this.resolveType(scope);
if (thisTb == null)
return null;
if (!scope.areTypesCompatible(thisTb, expectedTb)) {
scope.problemReporter().typeMismatchError(thisTb, expectedTb, this);
return null;
}
return thisTb;
}
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:"/*nonNLS*/ + constant.toString() + "*/ "/*nonNLS*/;
return s + toStringExpression(tab);
}
public String toStringExpression() {
//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)
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; }
}