X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java index d935982..0b6c568 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/Expression.java @@ -1,33 +1,527 @@ +/******************************************************************************* + * 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 - */ - public 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 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; + } }