1. Fix #1589403 - wrap double/single quoted strings
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / ConditionalExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
18 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
19
20 public class ConditionalExpression extends OperatorExpression {
21
22         public Expression condition, valueIfTrue, valueIfFalse;
23
24         public Constant optimizedBooleanConstant;
25
26         public Constant optimizedIfTrueConstant;
27
28         public Constant optimizedIfFalseConstant;
29
30         private int returnTypeSlotSize = 1;
31
32         // for local variables table attributes
33         int trueInitStateIndex = -1;
34
35         int falseInitStateIndex = -1;
36
37         int mergedInitStateIndex = -1;
38
39         public ConditionalExpression(Expression condition, Expression valueIfTrue,
40                         Expression valueIfFalse) {
41                 this.condition = condition;
42                 this.valueIfTrue = valueIfTrue;
43                 this.valueIfFalse = valueIfFalse;
44                 sourceStart = condition.sourceStart;
45                 sourceEnd = valueIfFalse.sourceEnd;
46         }
47
48         public FlowInfo analyseCode(BlockScope currentScope,
49                         FlowContext flowContext, FlowInfo flowInfo) {
50
51                 Constant cst = this.condition.optimizedBooleanConstant();
52                 boolean isConditionOptimizedTrue = cst != NotAConstant
53                                 && cst.booleanValue() == true;
54                 boolean isConditionOptimizedFalse = cst != NotAConstant
55                                 && cst.booleanValue() == false;
56
57                 int mode = flowInfo.reachMode();
58                 flowInfo = condition.analyseCode(currentScope, flowContext, flowInfo,
59                                 cst == NotAConstant);
60
61                 // process the if-true part
62                 FlowInfo trueFlowInfo = flowInfo.initsWhenTrue().copy();
63                 if (isConditionOptimizedFalse) {
64                         trueFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
65                 }
66                 trueInitStateIndex = currentScope.methodScope()
67                                 .recordInitializationStates(trueFlowInfo);
68                 trueFlowInfo = valueIfTrue.analyseCode(currentScope, flowContext,
69                                 trueFlowInfo);
70
71                 // process the if-false part
72                 FlowInfo falseFlowInfo = flowInfo.initsWhenFalse().copy();
73                 if (isConditionOptimizedTrue) {
74                         falseFlowInfo.setReachMode(FlowInfo.UNREACHABLE);
75                 }
76                 falseInitStateIndex = currentScope.methodScope()
77                                 .recordInitializationStates(falseFlowInfo);
78                 falseFlowInfo = valueIfFalse.analyseCode(currentScope, flowContext,
79                                 falseFlowInfo);
80
81                 // merge if-true & if-false initializations
82                 FlowInfo mergedInfo;
83                 if (isConditionOptimizedTrue) {
84                         mergedInfo = trueFlowInfo
85                                         .addPotentialInitializationsFrom(falseFlowInfo);
86                 } else if (isConditionOptimizedFalse) {
87                         mergedInfo = falseFlowInfo
88                                         .addPotentialInitializationsFrom(trueFlowInfo);
89                 } else {
90                         // merge using a conditional info - 1GK2BLM
91                         // if ((t && (v = t)) ? t : t && (v = f)) r = v; -- ok
92                         cst = this.optimizedIfTrueConstant;
93                         boolean isValueIfTrueOptimizedTrue = cst != null
94                                         && cst != NotAConstant && cst.booleanValue() == true;
95                         boolean isValueIfTrueOptimizedFalse = cst != null
96                                         && cst != NotAConstant && cst.booleanValue() == false;
97
98                         cst = this.optimizedIfFalseConstant;
99                         boolean isValueIfFalseOptimizedTrue = cst != null
100                                         && cst != NotAConstant && cst.booleanValue() == true;
101                         boolean isValueIfFalseOptimizedFalse = cst != null
102                                         && cst != NotAConstant && cst.booleanValue() == false;
103
104                         UnconditionalFlowInfo trueInfoWhenTrue = trueFlowInfo
105                                         .initsWhenTrue().copy().unconditionalInits();
106                         if (isValueIfTrueOptimizedFalse)
107                                 trueInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
108
109                         UnconditionalFlowInfo falseInfoWhenTrue = falseFlowInfo
110                                         .initsWhenTrue().copy().unconditionalInits();
111                         if (isValueIfFalseOptimizedFalse)
112                                 falseInfoWhenTrue.setReachMode(FlowInfo.UNREACHABLE);
113
114                         UnconditionalFlowInfo trueInfoWhenFalse = trueFlowInfo
115                                         .initsWhenFalse().copy().unconditionalInits();
116                         if (isValueIfTrueOptimizedTrue)
117                                 trueInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
118
119                         UnconditionalFlowInfo falseInfoWhenFalse = falseFlowInfo
120                                         .initsWhenFalse().copy().unconditionalInits();
121                         if (isValueIfFalseOptimizedTrue)
122                                 falseInfoWhenFalse.setReachMode(FlowInfo.UNREACHABLE);
123
124                         mergedInfo = FlowInfo.conditional(trueInfoWhenTrue
125                                         .mergedWith(falseInfoWhenTrue), trueInfoWhenFalse
126                                         .mergedWith(falseInfoWhenFalse));
127                 }
128                 mergedInitStateIndex = currentScope.methodScope()
129                                 .recordInitializationStates(mergedInfo);
130                 mergedInfo.setReachMode(mode);
131                 return mergedInfo;
132         }
133
134         /**
135          * Code generation for the conditional operator ?:
136          * 
137          * @param currentScope
138          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
139          * @param codeStream
140          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
141          * @param valueRequired
142          *            boolean
143          */
144         // public void generateCode(
145         // BlockScope currentScope,
146         // CodeStream codeStream,
147         // boolean valueRequired) {
148         //
149         // int pc = codeStream.position;
150         // Label endifLabel, falseLabel;
151         // if (constant != NotAConstant) {
152         // if (valueRequired)
153         // codeStream.generateConstant(constant, implicitConversion);
154         // codeStream.recordPositionsFrom(pc, this.sourceStart);
155         // return;
156         // }
157         // Constant cst = condition.constant;
158         // Constant condCst = condition.optimizedBooleanConstant();
159         // boolean needTruePart =
160         // !(((cst != NotAConstant) && (cst.booleanValue() == false))
161         // || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
162         // boolean needFalsePart =
163         // !(((cst != NotAConstant) && (cst.booleanValue() == true))
164         // || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
165         // endifLabel = new Label(codeStream);
166         //
167         // // Generate code for the condition
168         // boolean needConditionValue = (cst == NotAConstant) && (condCst ==
169         // NotAConstant);
170         // condition.generateOptimizedBoolean(
171         // currentScope,
172         // codeStream,
173         // null,
174         // (falseLabel = new Label(codeStream)),
175         // needConditionValue);
176         //
177         // if (trueInitStateIndex != -1) {
178         // codeStream.removeNotDefinitelyAssignedVariables(
179         // currentScope,
180         // trueInitStateIndex);
181         // codeStream.addDefinitelyAssignedVariables(currentScope,
182         // trueInitStateIndex);
183         // }
184         // // Then code generation
185         // if (needTruePart) {
186         // valueIfTrue.generateCode(currentScope, codeStream, valueRequired);
187         // if (needFalsePart) {
188         // // Jump over the else part
189         // int position = codeStream.position;
190         // codeStream.goto_(endifLabel);
191         // codeStream.updateLastRecordedEndPC(position);
192         // // Tune codestream stack size
193         // if (valueRequired) {
194         // codeStream.decrStackSize(returnTypeSlotSize);
195         // }
196         // }
197         // }
198         // if (needFalsePart) {
199         // falseLabel.place();
200         // if (falseInitStateIndex != -1) {
201         // codeStream.removeNotDefinitelyAssignedVariables(
202         // currentScope,
203         // falseInitStateIndex);
204         // codeStream.addDefinitelyAssignedVariables(currentScope,
205         // falseInitStateIndex);
206         // }
207         // valueIfFalse.generateCode(currentScope, codeStream, valueRequired);
208         // // End of if statement
209         // endifLabel.place();
210         // }
211         // // May loose some local variable initializations : affecting the local
212         // variable attributes
213         // if (mergedInitStateIndex != -1) {
214         // codeStream.removeNotDefinitelyAssignedVariables(
215         // currentScope,
216         // mergedInitStateIndex);
217         // }
218         // // implicit conversion
219         // if (valueRequired)
220         // codeStream.generateImplicitConversion(implicitConversion);
221         // codeStream.recordPositionsFrom(pc, this.sourceStart);
222         // }
223         //
224         // /**
225         // * Optimized boolean code generation for the conditional operator ?:
226         // */
227         // public void generateOptimizedBoolean(
228         // BlockScope currentScope,
229         // CodeStream codeStream,
230         // Label trueLabel,
231         // Label falseLabel,
232         // boolean valueRequired) {
233         //
234         // if ((constant != Constant.NotAConstant) && (constant.typeID() ==
235         // T_boolean) // constant
236         // || (valueIfTrue.implicitConversion >> 4) != T_boolean) { // non boolean
237         // values
238         // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
239         // falseLabel, valueRequired);
240         // return;
241         // }
242         // Constant cst = condition.constant;
243         // Constant condCst = condition.optimizedBooleanConstant();
244         // boolean needTruePart =
245         // !(((cst != NotAConstant) && (cst.booleanValue() == false))
246         // || ((condCst != NotAConstant) && (condCst.booleanValue() == false)));
247         // boolean needFalsePart =
248         // !(((cst != NotAConstant) && (cst.booleanValue() == true))
249         // || ((condCst != NotAConstant) && (condCst.booleanValue() == true)));
250         //
251         // Label internalFalseLabel, endifLabel = new Label(codeStream);
252         //
253         // // Generate code for the condition
254         // boolean needConditionValue = (cst == NotAConstant) && (condCst ==
255         // NotAConstant);
256         // condition.generateOptimizedBoolean(
257         // currentScope,
258         // codeStream,
259         // null,
260         // internalFalseLabel = new Label(codeStream),
261         // needConditionValue);
262         //
263         // if (trueInitStateIndex != -1) {
264         // codeStream.removeNotDefinitelyAssignedVariables(
265         // currentScope,
266         // trueInitStateIndex);
267         // codeStream.addDefinitelyAssignedVariables(currentScope,
268         // trueInitStateIndex);
269         // }
270         // // Then code generation
271         // if (needTruePart) {
272         // valueIfTrue.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
273         // falseLabel, valueRequired);
274         //                      
275         // if (needFalsePart) {
276         // // Jump over the else part
277         // int position = codeStream.position;
278         // codeStream.goto_(endifLabel);
279         // codeStream.updateLastRecordedEndPC(position);
280         // // No need to decrement codestream stack size
281         // // since valueIfTrue was already consumed by branch bytecode
282         // }
283         // }
284         // if (needFalsePart) {
285         // internalFalseLabel.place();
286         // if (falseInitStateIndex != -1) {
287         // codeStream.removeNotDefinitelyAssignedVariables(
288         // currentScope,
289         // falseInitStateIndex);
290         // codeStream.addDefinitelyAssignedVariables(currentScope,
291         // falseInitStateIndex);
292         // }
293         // valueIfFalse.generateOptimizedBoolean(currentScope, codeStream,
294         // trueLabel, falseLabel, valueRequired);
295         //
296         // // End of if statement
297         // endifLabel.place();
298         // }
299         // // May loose some local variable initializations : affecting the local
300         // variable attributes
301         // if (mergedInitStateIndex != -1) {
302         // codeStream.removeNotDefinitelyAssignedVariables(
303         // currentScope,
304         // mergedInitStateIndex);
305         // }
306         // // no implicit conversion for boolean values
307         // codeStream.updateLastRecordedEndPC(codeStream.position);
308         // }
309         //
310         // public Constant optimizedBooleanConstant() {
311         //
312         // return this.optimizedBooleanConstant == null ? this.constant :
313         // this.optimizedBooleanConstant;
314         // }
315         //      
316         // public TypeBinding resolveType(BlockScope scope) {
317         // // specs p.368
318         // constant = NotAConstant;
319         // TypeBinding conditionType = condition.resolveTypeExpecting(scope,
320         // BooleanBinding);
321         // TypeBinding valueIfTrueType = valueIfTrue.resolveType(scope);
322         // TypeBinding valueIfFalseType = valueIfFalse.resolveType(scope);
323         // if (conditionType == null || valueIfTrueType == null || valueIfFalseType
324         // == null)
325         // return null;
326         //
327         // // Propagate the constant value from the valueIfTrue and valueIFFalse
328         // expression if it is possible
329         // Constant condConstant, trueConstant, falseConstant;
330         // if ((condConstant = condition.constant) != NotAConstant
331         // && (trueConstant = valueIfTrue.constant) != NotAConstant
332         // && (falseConstant = valueIfFalse.constant) != NotAConstant) {
333         // // all terms are constant expression so we can propagate the constant
334         // // from valueIFTrue or valueIfFalse to teh receiver constant
335         // constant = condConstant.booleanValue() ? trueConstant : falseConstant;
336         // }
337         // if (valueIfTrueType == valueIfFalseType) { // harmed the implicit
338         // conversion
339         // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
340         // valueIfFalse.implicitConversion = valueIfTrue.implicitConversion;
341         // if (valueIfTrueType == LongBinding || valueIfTrueType == DoubleBinding) {
342         // returnTypeSlotSize = 2;
343         // }
344         //
345         // if (valueIfTrueType == BooleanBinding) {
346         // this.optimizedIfTrueConstant = valueIfTrue.optimizedBooleanConstant();
347         // this.optimizedIfFalseConstant = valueIfFalse.optimizedBooleanConstant();
348         //                      
349         // // Propagate the optimized boolean constant if possible
350         // if ((condConstant = condition.optimizedBooleanConstant()) !=
351         // NotAConstant) {
352         //                                      
353         // this.optimizedBooleanConstant = condConstant.booleanValue()
354         // ? optimizedIfTrueConstant
355         // : optimizedIfFalseConstant;
356         // }
357         // }
358         // return this.resolvedType = valueIfTrueType;
359         // }
360         // // Determine the return type depending on argument types
361         // // Numeric types
362         // if (valueIfTrueType.isNumericType() && valueIfFalseType.isNumericType())
363         // {
364         // // (Short x Byte) or (Byte x Short)"
365         // if ((valueIfTrueType == ByteBinding && valueIfFalseType == ShortBinding)
366         // || (valueIfTrueType == ShortBinding && valueIfFalseType == ByteBinding))
367         // {
368         // valueIfTrue.implicitWidening(ShortBinding, valueIfTrueType);
369         // valueIfFalse.implicitWidening(ShortBinding, valueIfFalseType);
370         // this.resolvedType = ShortBinding;
371         // return ShortBinding;
372         // }
373         // // <Byte|Short|Char> x constant(Int) ---> <Byte|Short|Char> and
374         // reciprocally
375         // if ((valueIfTrueType == ByteBinding || valueIfTrueType == ShortBinding ||
376         // valueIfTrueType == CharBinding)
377         // && (valueIfFalseType == IntBinding
378         // && valueIfFalse.isConstantValueOfTypeAssignableToType(valueIfFalseType,
379         // valueIfTrueType))) {
380         // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
381         // valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
382         // this.resolvedType = valueIfTrueType;
383         // return valueIfTrueType;
384         // }
385         // if ((valueIfFalseType == ByteBinding
386         // || valueIfFalseType == ShortBinding
387         // || valueIfFalseType == CharBinding)
388         // && (valueIfTrueType == IntBinding
389         // && valueIfTrue.isConstantValueOfTypeAssignableToType(valueIfTrueType,
390         // valueIfFalseType))) {
391         // valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
392         // valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
393         // this.resolvedType = valueIfFalseType;
394         // return valueIfFalseType;
395         // }
396         // // Manual binary numeric promotion
397         // // int
398         // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_int)
399         // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_int)) {
400         // valueIfTrue.implicitWidening(IntBinding, valueIfTrueType);
401         // valueIfFalse.implicitWidening(IntBinding, valueIfFalseType);
402         // this.resolvedType = IntBinding;
403         // return IntBinding;
404         // }
405         // // long
406         // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_long)
407         // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_long)) {
408         // valueIfTrue.implicitWidening(LongBinding, valueIfTrueType);
409         // valueIfFalse.implicitWidening(LongBinding, valueIfFalseType);
410         // returnTypeSlotSize = 2;
411         // this.resolvedType = LongBinding;
412         // return LongBinding;
413         // }
414         // // float
415         // if (BaseTypeBinding.isNarrowing(valueIfTrueType.id, T_float)
416         // && BaseTypeBinding.isNarrowing(valueIfFalseType.id, T_float)) {
417         // valueIfTrue.implicitWidening(FloatBinding, valueIfTrueType);
418         // valueIfFalse.implicitWidening(FloatBinding, valueIfFalseType);
419         // this.resolvedType = FloatBinding;
420         // return FloatBinding;
421         // }
422         // // double
423         // valueIfTrue.implicitWidening(DoubleBinding, valueIfTrueType);
424         // valueIfFalse.implicitWidening(DoubleBinding, valueIfFalseType);
425         // returnTypeSlotSize = 2;
426         // this.resolvedType = DoubleBinding;
427         // return DoubleBinding;
428         // }
429         // // Type references (null null is already tested)
430         // if ((valueIfTrueType.isBaseType() && valueIfTrueType != NullBinding)
431         // || (valueIfFalseType.isBaseType() && valueIfFalseType != NullBinding)) {
432         // scope.problemReporter().conditionalArgumentsIncompatibleTypes(
433         // this,
434         // valueIfTrueType,
435         // valueIfFalseType);
436         // return null;
437         // }
438         // if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) {
439         // valueIfTrue.implicitWidening(valueIfTrueType, valueIfTrueType);
440         // valueIfFalse.implicitWidening(valueIfTrueType, valueIfFalseType);
441         // this.resolvedType = valueIfTrueType;
442         // return valueIfTrueType;
443         // }
444         // if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) {
445         // valueIfTrue.implicitWidening(valueIfFalseType, valueIfTrueType);
446         // valueIfFalse.implicitWidening(valueIfFalseType, valueIfFalseType);
447         // this.resolvedType = valueIfFalseType;
448         // return valueIfFalseType;
449         // }
450         // scope.problemReporter().conditionalArgumentsIncompatibleTypes(
451         // this,
452         // valueIfTrueType,
453         // valueIfFalseType);
454         // return null;
455         // }
456         public StringBuffer printExpressionNoParenthesis(int indent,
457                         StringBuffer output) {
458
459                 condition.printExpression(indent, output).append(" ? "); //$NON-NLS-1$
460                 valueIfTrue.printExpression(0, output).append(" : "); //$NON-NLS-1$
461                 return valueIfFalse.printExpression(0, output);
462         }
463
464         public String toStringExpressionNoParenthesis() {
465                 return condition.toStringExpression() + " ? " + //$NON-NLS-1$
466                                 valueIfTrue.toStringExpression() + " : " + //$NON-NLS-1$
467                                 valueIfFalse.toStringExpression();
468         }
469
470         public void traverse(ASTVisitor visitor, BlockScope scope) {
471                 if (visitor.visit(this, scope)) {
472                         condition.traverse(visitor, scope);
473                         valueIfTrue.traverse(visitor, scope);
474                         valueIfFalse.traverse(visitor, scope);
475                 }
476                 visitor.endVisit(this, scope);
477         }
478 }