/******************************************************************************* * 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.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; public class ConditionalExpression extends OperatorExpression { 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); } }