X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/ConditionalExpression.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/ConditionalExpression.java index 121a158..39b98a3 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/ConditionalExpression.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/ast/ConditionalExpression.java @@ -1,64 +1,478 @@ +/******************************************************************************* + * 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 java.util.List; -import java.util.ArrayList; +import net.sourceforge.phpdt.internal.compiler.ASTVisitor; +import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; +import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; +import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo; +import net.sourceforge.phpdt.internal.compiler.impl.Constant; +import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; -/** - * A ConditionalExpression is like that : booleanExpression ? trueValue : falseValue; - * @author Matthieu Casanova - */ public class ConditionalExpression extends OperatorExpression { - public Expression condition, valueIfTrue, valueIfFalse; - - public ConditionalExpression(final Expression condition, - final Expression valueIfTrue, - final Expression valueIfFalse) { - super(-1, condition.sourceStart, valueIfFalse.sourceEnd); - this.condition = condition; - this.valueIfTrue = valueIfTrue; - this.valueIfFalse = valueIfFalse; - } - - public String toStringExpression() { - final StringBuffer buff = new StringBuffer("("); - buff.append(condition.toStringExpression()); - buff.append(") ? "); - buff.append(valueIfTrue.toStringExpression()); - buff.append(" : "); - buff.append(valueIfFalse.toStringExpression()); - return buff.toString(); - } - - /** - * Get the variables from outside (parameters, globals ...) - * @return the variables from outside - */ - public List getOutsideVariable() { - return new ArrayList(); - } - - /** - * get the modified variables. - * @return the variables from we change value - */ - public List getModifiedVariable() { - final ArrayList list = new ArrayList(); - list.addAll(condition.getModifiedVariable()); - list.addAll(valueIfTrue.getModifiedVariable()); - list.addAll(valueIfFalse.getModifiedVariable()); - return list; - } - - /** - * Get the variables used. - * @return the variables used - */ - public List getUsedVariable() { - final ArrayList list = new ArrayList(); - list.addAll(condition.getUsedVariable()); - list.addAll(valueIfTrue.getUsedVariable()); - list.addAll(valueIfFalse.getUsedVariable()); - return list; - } + public Expression condition, valueIfTrue, valueIfFalse; + + public Constant optimizedBooleanConstant; + + public Constant optimizedIfTrueConstant; + + public Constant optimizedIfFalseConstant; + + //private int returnTypeSlotSize = 1; + + // for local variables table attributes + int trueInitStateIndex = -1; + + int falseInitStateIndex = -1; + + int mergedInitStateIndex = -1; + + public ConditionalExpression(Expression condition, Expression valueIfTrue, + Expression valueIfFalse) { + this.condition = condition; + this.valueIfTrue = valueIfTrue; + this.valueIfFalse = valueIfFalse; + sourceStart = condition.sourceStart; + sourceEnd = valueIfFalse.sourceEnd; + } + + public FlowInfo analyseCode(BlockScope currentScope, + FlowContext flowContext, FlowInfo flowInfo) { + + Constant cst = this.condition.optimizedBooleanConstant(); + boolean isConditionOptimizedTrue = cst != NotAConstant + && cst.booleanValue() == true; + boolean isConditionOptimizedFalse = cst != NotAConstant + && cst.booleanValue() == false; + + int mode = flowInfo.reachMode(); + flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo, + cst == NotAConstant); + + // process the if-true part + FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy(); + if (isConditionOptimizedFalse) { + trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE); + } + trueInitStateIndex = currentScope.methodScope() + .recordInitializationStates(trueFlowInfo); + trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext, + trueFlowInfo); + + // process the if-false part + FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy(); + if (isConditionOptimizedTrue) { + falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE); + } + falseInitStateIndex = currentScope.methodScope() + .recordInitializationStates(falseFlowInfo); + falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext, + falseFlowInfo); + + // merge if-true & if-false initializations + FlowInfo mergedInfo; + if (isConditionOptimizedTrue) { + mergedInfo = trueFlowInfo + .addPotentialInitializationsFrom(falseFlowInfo); + } else if (isConditionOptimizedFalse) { + mergedInfo = falseFlowInfo + .addPotentialInitializationsFrom(trueFlowInfo); + } else { + // merge using a conditional info - 1GK2BLM + // if ((t && (v = t)) ? t : t && (v = f)) r = v; -- ok + cst = this.optimizedIfTrueConstant; + boolean isValueIfTrueOptimizedTrue = cst != null + && cst != NotAConstant && cst.booleanValue() == true; + boolean isValueIfTrueOptimizedFalse = cst != null + && cst != NotAConstant && cst.booleanValue() == false; + + cst = this.optimizedIfFalseConstant; + boolean isValueIfFalseOptimizedTrue = cst != null + && cst != NotAConstant && cst.booleanValue() == true; + boolean isValueIfFalseOptimizedFalse = cst != null + && cst != NotAConstant && cst.booleanValue() == false; + + UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo + .initsWhenTrue().copy().unconditionalInits(); + if (isValueIfTrueOptimizedFalse) + trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); + + UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo + .initsWhenTrue().copy().unconditionalInits(); + if (isValueIfFalseOptimizedFalse) + falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE); + + UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo + .initsWhenFalse().copy().unconditionalInits(); + if (isValueIfTrueOptimizedTrue) + trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); + + UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo + .initsWhenFalse().copy().unconditionalInits(); + if (isValueIfFalseOptimizedTrue) + falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE); + + mergedInfo = FlowInfo.conditional(trueInfoWhenTrue + .mergedWith(falseInfoWhenTrue), trueInfoWhenFalse + .mergedWith(falseInfoWhenFalse)); + } + mergedInitStateIndex = currentScope.methodScope() + .recordInitializationStates(mergedInfo); + mergedInfo.setReachMode(mode); + return mergedInfo; + } + + /** + * Code generation for the conditional operator ?: + * + * @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) { + // + // int pc = codeStream.position; + // Label endifLabel, falseLabel; + // if (constant != NotAConstant) { + // if (valueRequired) + // codeStream.generateConstant(constant, implicitConversion); + // codeStream.recordPositionsFrom(pc, this.sourceStart); + // return; + // } + // Constant cst = condition.constant; + // Constant condCst = condition.optimizedBooleanConstant(); + // boolean needTruePart = + // !(((cst != NotAConstant) && (cst.booleanValue() == false)) + // || ((condCst != NotAConstant) && (condCst.booleanValue() == false))); + // boolean needFalsePart = + // !(((cst != NotAConstant) && (cst.booleanValue() == true)) + // || ((condCst != NotAConstant) && (condCst.booleanValue() == true))); + // endifLabel = new Label(codeStream); + // + // // Generate code for the condition + // boolean needConditionValue = (cst == NotAConstant) && (condCst == + // NotAConstant); + // condition.generateOptimizedBoolean( + // currentScope, + // codeStream, + // null, + // (falseLabel = new Label(codeStream)), + // needConditionValue); + // + // if (trueInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // trueInitStateIndex); + // codeStream.addDefinitelyAssignedVariables(currentScope, + // trueInitStateIndex); + // } + // // Then code generation + // if (needTruePart) { + // valueIfTrue.generateCode(currentScope, codeStream, valueRequired); + // if (needFalsePart) { + // // Jump over the else part + // int position = codeStream.position; + // codeStream.goto_(endifLabel); + // codeStream.updateLastRecordedEndPC(position); + // // Tune codestream stack size + // if (valueRequired) { + // codeStream.decrStackSize(returnTypeSlotSize); + // } + // } + // } + // if (needFalsePart) { + // falseLabel.place(); + // if (falseInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // falseInitStateIndex); + // codeStream.addDefinitelyAssignedVariables(currentScope, + // falseInitStateIndex); + // } + // valueIfFalse.generateCode(currentScope, codeStream, valueRequired); + // // End of if statement + // endifLabel.place(); + // } + // // May loose some local variable initializations : affecting the local + // variable attributes + // if (mergedInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // mergedInitStateIndex); + // } + // // implicit conversion + // if (valueRequired) + // codeStream.generateImplicitConversion(implicitConversion); + // codeStream.recordPositionsFrom(pc, this.sourceStart); + // } + // + // /** + // * Optimized boolean code generation for the conditional operator ?: + // */ + // public void generateOptimizedBoolean( + // BlockScope currentScope, + // CodeStream codeStream, + // Label trueLabel, + // Label falseLabel, + // boolean valueRequired) { + // + // if ((constant != Constant.NotAConstant) && (constant.typeID() == + // T_boolean) // constant + // || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean + // values + // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, + // falseLabel, valueRequired); + // return; + // } + // Constant cst = condition.constant; + // Constant condCst = condition.optimizedBooleanConstant(); + // boolean needTruePart = + // !(((cst != NotAConstant) && (cst.booleanValue() == false)) + // || ((condCst != NotAConstant) && (condCst.booleanValue() == false))); + // boolean needFalsePart = + // !(((cst != NotAConstant) && (cst.booleanValue() == true)) + // || ((condCst != NotAConstant) && (condCst.booleanValue() == true))); + // + // Label internalFalseLabel, endifLabel = new Label(codeStream); + // + // // Generate code for the condition + // boolean needConditionValue = (cst == NotAConstant) && (condCst == + // NotAConstant); + // condition.generateOptimizedBoolean( + // currentScope, + // codeStream, + // null, + // internalFalseLabel = new Label(codeStream), + // needConditionValue); + // + // if (trueInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // trueInitStateIndex); + // codeStream.addDefinitelyAssignedVariables(currentScope, + // trueInitStateIndex); + // } + // // Then code generation + // if (needTruePart) { + // valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel, + // falseLabel, valueRequired); + // + // if (needFalsePart) { + // // Jump over the else part + // int position = codeStream.position; + // codeStream.goto_(endifLabel); + // codeStream.updateLastRecordedEndPC(position); + // // No need to decrement codestream stack size + // // since valueIfTrue was already consumed by branch bytecode + // } + // } + // if (needFalsePart) { + // internalFalseLabel.place(); + // if (falseInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // falseInitStateIndex); + // codeStream.addDefinitelyAssignedVariables(currentScope, + // falseInitStateIndex); + // } + // valueIfFalse.generateOptimizedBoolean(currentScope, codeStream, + // trueLabel, falseLabel, valueRequired); + // + // // End of if statement + // endifLabel.place(); + // } + // // May loose some local variable initializations : affecting the local + // variable attributes + // if (mergedInitStateIndex != -1) { + // codeStream.removeNotDefinitelyAssignedVariables( + // currentScope, + // mergedInitStateIndex); + // } + // // no implicit conversion for boolean values + // codeStream.updateLastRecordedEndPC(codeStream.position); + // } + // + // public Constant optimizedBooleanConstant() { + // + // return this.optimizedBooleanConstant == null ? this.constant : + // this.optimizedBooleanConstant; + // } + // + // public TypeBinding resolveType(BlockScope scope) { + // // specs p.368 + // constant = NotAConstant; + // TypeBinding conditionType = condition.resolveTypeExpecting(scope, + // BooleanBinding); + // TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope); + // TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope); + // if (conditionType == null || valueIfTrueType == null || valueIfFalseType + // == null) + // return null; + // + // // Propagate the constant value from the valueIfTrue and valueIFFalse + // expression if it is possible + // Constant condConstant, trueConstant, falseConstant; + // if ((condConstant = condition.constant) != NotAConstant + // && (trueConstant = valueIfTrue.constant) != NotAConstant + // && (falseConstant = valueIfFalse.constant) != NotAConstant) { + // // all terms are constant expression so we can propagate the constant + // // from valueIFTrue or valueIfFalse to teh receiver constant + // constant = condConstant.booleanValue() ? trueConstant : falseConstant; + // } + // if (valueIfTrueType == valueIfFalseType) { // harmed the implicit + // conversion + // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); + // valueIfFalse.implicitConversion = valueIfTrue.implicitConversion; + // if (valueIfTrueType == LongBinding || valueIfTrueType == DoubleBinding) { + // returnTypeSlotSize = 2; + // } + // + // if (valueIfTrueType == BooleanBinding) { + // this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant(); + // this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant(); + // + // // Propagate the optimized boolean constant if possible + // if ((condConstant = condition.optimizedBooleanConstant()) != + // NotAConstant) { + // + // this.optimizedBooleanConstant = condConstant.booleanValue() + // ? optimizedIfTrueConstant + // : optimizedIfFalseConstant; + // } + // } + // return this.resolvedType = valueIfTrueType; + // } + // // Determine the return type depending on argument types + // // Numeric types + // if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType()) + // { + // // (Short x Byte) or (Byte x Short)" + // if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding) + // || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding)) + // { + // valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType); + // valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType); + // this.resolvedType = ShortBinding; + // return ShortBinding; + // } + // // x constant(Int) ---> and + // reciprocally + // if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding || + // valueIfTrueType == CharBinding) + // && (valueIfFalseType == IntBinding + // && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType, + // valueIfTrueType))) { + // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); + // valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType); + // this.resolvedType = valueIfTrueType; + // return valueIfTrueType; + // } + // if ((valueIfFalseType == ByteBinding + // || valueIfFalseType == ShortBinding + // || valueIfFalseType == CharBinding) + // && (valueIfTrueType == IntBinding + // && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType, + // valueIfFalseType))) { + // valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType); + // valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType); + // this.resolvedType = valueIfFalseType; + // return valueIfFalseType; + // } + // // Manual binary numeric promotion + // // int + // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int) + // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) { + // valueIfTrue.implicitWidening(IntBinding, valueIfTrueType); + // valueIfFalse.implicitWidening(IntBinding, valueIfFalseType); + // this.resolvedType = IntBinding; + // return IntBinding; + // } + // // long + // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long) + // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) { + // valueIfTrue.implicitWidening(LongBinding, valueIfTrueType); + // valueIfFalse.implicitWidening(LongBinding, valueIfFalseType); + // returnTypeSlotSize = 2; + // this.resolvedType = LongBinding; + // return LongBinding; + // } + // // float + // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float) + // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) { + // valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType); + // valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType); + // this.resolvedType = FloatBinding; + // return FloatBinding; + // } + // // double + // valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType); + // valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType); + // returnTypeSlotSize = 2; + // this.resolvedType = DoubleBinding; + // return DoubleBinding; + // } + // // Type references (null null is already tested) + // if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding) + // || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) { + // scope.problemReporter().conditionalArgumentsIncompatibleTypes( + // this, + // valueIfTrueType, + // valueIfFalseType); + // return null; + // } + // if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) { + // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType); + // valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType); + // this.resolvedType = valueIfTrueType; + // return valueIfTrueType; + // } + // if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) { + // valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType); + // valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType); + // this.resolvedType = valueIfFalseType; + // return valueIfFalseType; + // } + // scope.problemReporter().conditionalArgumentsIncompatibleTypes( + // this, + // valueIfTrueType, + // valueIfFalseType); + // return null; + // } + public StringBuffer printExpressionNoParenthesis(int indent, + StringBuffer output) { + + condition.printExpression(indent, output).append(" ? "); //$NON-NLS-1$ + valueIfTrue.printExpression(0, output).append(" : "); //$NON-NLS-1$ + return valueIfFalse.printExpression(0, output); + } + + public String toStringExpressionNoParenthesis() { + return condition.toStringExpression() + " ? " + //$NON-NLS-1$ + valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$ + valueIfFalse.toStringExpression(); + } + + public void traverse(ASTVisitor visitor, BlockScope scope) { + if (visitor.visit(this, scope)) { + condition.traverse(visitor, scope); + valueIfTrue.traverse(visitor, scope); + valueIfFalse.traverse(visitor, scope); + } + visitor.endVisit(this, scope); + } }