/******************************************************************************* * 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; 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; } }