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..9bd4807 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,427 @@ +/******************************************************************************* + * 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 Expression condition, valueIfTrue, valueIfFalse; + public Constant optimizedBooleanConstant; + public Constant optimizedIfTrueConstant; + public Constant optimizedIfFalseConstant; + + private int returnTypeSlotSize = 1; - 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; - } + // 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 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(); - } + public FlowInfo analyseCode( + BlockScope currentScope, + FlowContext flowContext, + FlowInfo flowInfo) { - /** - * Get the variables from outside (parameters, globals ...) - * @return the variables from outside - */ - public List getOutsideVariable() { - return new ArrayList(); - } + Constant cst = this.condition.optimizedBooleanConstant(); + boolean isConditionOptimizedTrue = cst != NotAConstant && cst.booleanValue() == true; + boolean isConditionOptimizedFalse = cst != NotAConstant && cst.booleanValue() == false; - /** - * 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; - } + 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); - /** - * 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; - } + // 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); + } }