View | Details | Raw Unified | Return to bug 102728 | Differences between
and this patch

Collapse All | Expand All

(-)compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java (-5 / +156 lines)
Lines 18-23 Link Here
18
import org.eclipse.jdt.internal.compiler.lookup.*;
18
import org.eclipse.jdt.internal.compiler.lookup.*;
19
19
20
public class BinaryExpression extends OperatorExpression {
20
public class BinaryExpression extends OperatorExpression {
21
	
22
/* Tracking helpers
23
 * The following are used to elaborate realistic statistics about binary 
24
 * expressions. This must be neutralized in the released code.
25
 * Search the keyword BE_INSTRUMENTATION to reenable.
26
 * An external device must install a suitable probe so as to monitor the
27
 * emission of events and publish the results.
28
	public interface Probe {
29
		public void ping(int depth);
30
	}
31
	public int depthTracker;
32
	public static Probe probe;
33
 */
21
34
22
	public Expression left, right;
35
	public Expression left, right;
23
	public Constant optimizedBooleanConstant;
36
	public Constant optimizedBooleanConstant;
Lines 28-36 Link Here
28
	this.bits |= operator << ASTNode.OperatorSHIFT; // encode operator
41
	this.bits |= operator << ASTNode.OperatorSHIFT; // encode operator
29
	this.sourceStart = left.sourceStart;
42
	this.sourceStart = left.sourceStart;
30
	this.sourceEnd = right.sourceEnd;
43
	this.sourceEnd = right.sourceEnd;
44
	// BE_INSTRUMENTATION: neutralized in the released code
45
//	if (left instanceof BinaryExpression && 
46
//			((left.bits & OperatorMASK) ^ (this.bits & OperatorMASK)) == 0) {
47
//		this.depthTracker = ((BinaryExpression)left).depthTracker + 1;
48
//	} else {
49
//		this.depthTracker = 1;
50
//	}
31
}
51
}
32
52
33
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
53
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, 
54
		FlowInfo flowInfo) {
55
	// keep implementation in sync with CombinedBinaryExpression#analyseCode
34
	if (this.resolvedType.id == TypeIds.T_JavaLangString) {
56
	if (this.resolvedType.id == TypeIds.T_JavaLangString) {
35
		return this.right.analyseCode(
57
		return this.right.analyseCode(
36
							currentScope, flowContext, 
58
							currentScope, flowContext, 
Lines 78-83 Link Here
78
/**
100
/**
79
 * Code generation for a binary operation
101
 * Code generation for a binary operation
80
 */
102
 */
103
// given the current focus of CombinedBinaryExpression on strings concatenation,
104
// we do not provide a general, non-recursive implementation of generateCode,
105
// but rely upon generateOptimizedStringConcatenationCreation instead
81
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
106
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
82
	int pc = codeStream.position;
107
	int pc = codeStream.position;
83
	if (this.constant != Constant.NotAConstant) {
108
	if (this.constant != Constant.NotAConstant) {
Lines 90-95 Link Here
90
		case PLUS :
115
		case PLUS :
91
			switch (this.bits & ASTNode.ReturnTypeIDMASK) {
116
			switch (this.bits & ASTNode.ReturnTypeIDMASK) {
92
				case T_JavaLangString :
117
				case T_JavaLangString :
118
					// BE_INSTRUMENTATION: neutralized in the released code					
119
//					if (probe != null) {
120
//						probe.ping(this.depthTracker);
121
//					}
93
					codeStream.generateStringConcatenationAppend(currentScope, this.left, this.right);
122
					codeStream.generateStringConcatenationAppend(currentScope, this.left, this.right);
94
					if (!valueRequired)
123
					if (!valueRequired)
95
						codeStream.pop();
124
						codeStream.pop();
Lines 1511-1517 Link Here
1511
}
1540
}
1512
1541
1513
public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) {
1542
public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) {
1514
		
1543
	// keep implementation in sync with CombinedBinaryExpression
1544
	// #generateOptimizedStringConcatenation
1515
	/* In the case trying to make a string concatenation, there is no need to create a new
1545
	/* In the case trying to make a string concatenation, there is no need to create a new
1516
	 * string buffer, thus use a lower-level API for code generation involving only the
1546
	 * string buffer, thus use a lower-level API for code generation involving only the
1517
	 * appending of arguments to the existing StringBuffer
1547
	 * appending of arguments to the existing StringBuffer
Lines 1542-1553 Link Here
1542
}
1572
}
1543
1573
1544
public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, CodeStream codeStream, int typeID) {
1574
public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, CodeStream codeStream, int typeID) {
1545
		
1575
	// keep implementation in sync with CombinedBinaryExpression
1576
	// #generateOptimizedStringConcatenationCreation
1546
	/* In the case trying to make a string concatenation, there is no need to create a new
1577
	/* In the case trying to make a string concatenation, there is no need to create a new
1547
	 * string buffer, thus use a lower-level API for code generation involving only the 
1578
	 * string buffer, thus use a lower-level API for code generation involving only the 
1548
	 * appending of arguments to the existing StringBuffer
1579
	 * appending of arguments to the existing StringBuffer
1549
	 */
1580
	 */
1550
1551
	if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
1581
	if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
1552
		&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
1582
		&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
1553
		if (this.constant != Constant.NotAConstant) {
1583
		if (this.constant != Constant.NotAConstant) {
Lines 1579-1584 Link Here
1579
	return true;
1609
	return true;
1580
}
1610
}
1581
1611
1612
/**
1613
 * Separates into a reusable method the subpart of {@link 
1614
 * #resolveType(BlockScope)} that needs to be executed while climbing up the 
1615
 * chain of expressions of this' leftmost branch. For use by {@link 
1616
 * CombinedBinaryExpression#resolveType(BlockScope)}.
1617
 * @param scope the scope within which the resolution occurs
1618
 */
1619
void nonRecursiveResolveTypeUpwards(BlockScope scope) {
1620
	// keep implementation in sync with BinaryExpression#resolveType
1621
	boolean leftIsCast, rightIsCast;
1622
	TypeBinding leftType = this.left.resolvedType;
1623
	
1624
	if ((rightIsCast = this.right instanceof CastExpression) == true) {
1625
		this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
1626
	}
1627
	TypeBinding rightType = this.right.resolveType(scope);
1628
1629
	// use the id of the type to navigate into the table
1630
	if (leftType == null || rightType == null) {
1631
		this.constant = Constant.NotAConstant;
1632
		return;
1633
	}
1634
1635
	int leftTypeID = leftType.id;
1636
	int rightTypeID = rightType.id;
1637
1638
	// autoboxing support
1639
	boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
1640
	if (use15specifics) {
1641
		if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString && rightTypeID != TypeIds.T_null) {
1642
			leftTypeID = scope.environment().computeBoxingType(leftType).id;
1643
		}
1644
		if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) {
1645
			rightTypeID = scope.environment().computeBoxingType(rightType).id;
1646
		}
1647
	}
1648
	if (leftTypeID > 15
1649
		|| rightTypeID > 15) { // must convert String + Object || Object + String
1650
		if (leftTypeID == TypeIds.T_JavaLangString) {
1651
			rightTypeID = TypeIds.T_JavaLangObject;
1652
		} else if (rightTypeID == TypeIds.T_JavaLangString) {
1653
			leftTypeID = TypeIds.T_JavaLangObject;
1654
		} else {
1655
			this.constant = Constant.NotAConstant;
1656
			scope.problemReporter().invalidOperator(this, leftType, rightType);
1657
			return;
1658
		}
1659
	}
1660
	if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
1661
		if (leftTypeID == TypeIds.T_JavaLangString) {
1662
			this.left.computeConversion(scope, leftType, leftType);
1663
			if (rightType.isArrayType() && ((ArrayBinding) rightType).elementsType() == TypeBinding.CHAR) {
1664
				scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
1665
			}
1666
		}
1667
		if (rightTypeID == TypeIds.T_JavaLangString) {
1668
			this.right.computeConversion(scope, rightType, rightType);
1669
			if (leftType.isArrayType() && ((ArrayBinding) leftType).elementsType() == TypeBinding.CHAR) {
1670
				scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left);
1671
			}
1672
		}
1673
	}
1674
1675
	// the code is an int
1676
	// (cast)  left   Op (cast)  right --> result
1677
	//  0000   0000       0000   0000      0000
1678
	//  <<16   <<12       <<8    <<4       <<0
1679
1680
	// Don't test for result = 0. If it is zero, some more work is done.
1681
	// On the one hand when it is not zero (correct code) we avoid doing the test	
1682
	int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
1683
	int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];
1684
1685
	this.left.computeConversion(scope, 	TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType);
1686
	this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType);
1687
	this.bits |= operatorSignature & 0xF;
1688
	switch (operatorSignature & 0xF) { // record the current ReturnTypeID
1689
		// only switch on possible result type.....
1690
		case T_boolean :
1691
			this.resolvedType = TypeBinding.BOOLEAN;
1692
			break;
1693
		case T_byte :
1694
			this.resolvedType = TypeBinding.BYTE;
1695
			break;
1696
		case T_char :
1697
			this.resolvedType = TypeBinding.CHAR;
1698
			break;
1699
		case T_double :
1700
			this.resolvedType = TypeBinding.DOUBLE;
1701
			break;
1702
		case T_float :
1703
			this.resolvedType = TypeBinding.FLOAT;
1704
			break;
1705
		case T_int :
1706
			this.resolvedType = TypeBinding.INT;
1707
			break;
1708
		case T_long :
1709
			this.resolvedType = TypeBinding.LONG;
1710
			break;
1711
		case T_JavaLangString :
1712
			this.resolvedType = scope.getJavaLangString();
1713
			break;
1714
		default : //error........
1715
			this.constant = Constant.NotAConstant;
1716
			scope.problemReporter().invalidOperator(this, leftType, rightType);
1717
			return;
1718
	}
1719
1720
	// check need for operand cast
1721
	if ((leftIsCast = (this.left instanceof CastExpression)) == true || 
1722
			rightIsCast) {
1723
		CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID, leftIsCast, this.right, rightTypeID, rightIsCast);
1724
	}
1725
	// compute the constant when valid
1726
	computeConstant(scope, leftTypeID, rightTypeID);
1727
}
1728
1582
public void optimizedBooleanConstant(int leftId, int operator, int rightId) {
1729
public void optimizedBooleanConstant(int leftId, int operator, int rightId) {
1583
	switch (operator) {
1730
	switch (operator) {
1584
		case AND :
1731
		case AND :
Lines 1628-1638 Link Here
1628
}
1775
}
1629
1776
1630
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
1777
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
1778
	// keep implementation in sync with 
1779
	// CombinedBinaryExpression#printExpressionNoParenthesis
1631
	this.left.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
1780
	this.left.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
1632
	return this.right.printExpression(0, output);
1781
	return this.right.printExpression(0, output);
1633
}
1782
}
1634
	
1783
1635
public TypeBinding resolveType(BlockScope scope) {
1784
public TypeBinding resolveType(BlockScope scope) {
1785
	// keep implementation in sync with CombinedBinaryExpression#resolveType
1786
	// and nonRecursiveResolveTypeUpwards
1636
	boolean leftIsCast, rightIsCast;
1787
	boolean leftIsCast, rightIsCast;
1637
	if ((leftIsCast = this.left instanceof CastExpression) == true) this.left.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
1788
	if ((leftIsCast = this.left instanceof CastExpression) == true) this.left.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
1638
	TypeBinding leftType = this.left.resolveType(scope);
1789
	TypeBinding leftType = this.left.resolveType(scope);
(-)compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java (-12 / +69 lines)
Lines 1528-1535 Link Here
1528
					} else {
1528
					} else {
1529
						this.expressionStack[this.expressionPtr] = new BinaryExpression(expr1, expr2, PLUS);
1529
						this.expressionStack[this.expressionPtr] = new BinaryExpression(expr1, expr2, PLUS);
1530
					}
1530
					}
1531
				} else if (expr1 instanceof CombinedBinaryExpression) {
1532
					CombinedBinaryExpression cursor;
1533
					// left branch is comprised of PLUS BEs
1534
					// cursor is shifted upwards, while needed BEs are added
1535
					// on demand; past the arityMax-th
1536
					// consecutive BE, a CBE is inserted that holds a 
1537
					// full-fledged references table
1538
					if ((cursor = (CombinedBinaryExpression)expr1).arity <
1539
								cursor.arityMax) {
1540
						cursor.left = new BinaryExpression(cursor.left,
1541
								cursor.right, PLUS);
1542
						cursor.arity++;
1543
					} else {
1544
						cursor.left = new CombinedBinaryExpression(cursor.left,
1545
								cursor.right, PLUS, cursor.arity);
1546
						cursor.arity = 0;
1547
						cursor.tuneArityMax();
1548
					}
1549
					cursor.right = expr2;
1550
					cursor.sourceEnd = expr2.sourceEnd;
1551
					this.expressionStack[this.expressionPtr] = cursor;
1552
					// BE_INSTRUMENTATION: neutralized in the released code					
1553
//					cursor.depthTracker = ((BinaryExpression)cursor.left).
1554
//						depthTracker + 1;					
1555
				} else if (expr1 instanceof BinaryExpression &&
1556
							// single out the a + b case, which is a BE 
1557
							// instead of a CBE (slightly more than a half of 
1558
							// strings concatenation are one-deep binary 
1559
							// expressions)
1560
						((expr1.bits & ASTNode.OperatorMASK) >> 
1561
							ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
1562
					this.expressionStack[this.expressionPtr] = 
1563
						new CombinedBinaryExpression(expr1, expr2, PLUS, 1);
1531
				} else {
1564
				} else {
1532
					this.expressionStack[this.expressionPtr] = new BinaryExpression(expr1, expr2, PLUS);
1565
					this.expressionStack[this.expressionPtr] = 
1566
						new BinaryExpression(expr1, expr2, PLUS);
1533
				}
1567
				}
1534
			} else if (expr1 instanceof StringLiteral) {
1568
			} else if (expr1 instanceof StringLiteral) {
1535
				if (expr2 instanceof StringLiteral) {
1569
				if (expr2 instanceof StringLiteral) {
Lines 1537-1555 Link Here
1537
					this.expressionStack[this.expressionPtr] = 
1571
					this.expressionStack[this.expressionPtr] = 
1538
						((StringLiteral) expr1).extendsWith((StringLiteral) expr2); 
1572
						((StringLiteral) expr1).extendsWith((StringLiteral) expr2); 
1539
				} else {
1573
				} else {
1574
					// single out the a + b case
1540
					this.expressionStack[this.expressionPtr] = 
1575
					this.expressionStack[this.expressionPtr] = 
1541
						new BinaryExpression(
1576
						new BinaryExpression(expr1, expr2, PLUS);
1542
							expr1, 
1577
				}
1543
							expr2, 
1578
			} else if (expr1 instanceof CombinedBinaryExpression) {
1544
							op);
1579
					CombinedBinaryExpression cursor;
1580
					// shift cursor; create BE/CBE as needed
1581
					if ((cursor = (CombinedBinaryExpression)expr1).arity <
1582
								cursor.arityMax) {
1583
						cursor.left = new BinaryExpression(cursor.left,
1584
								cursor.right, PLUS);
1585
						cursor.arity++;
1586
					} else {
1587
						cursor.left = new CombinedBinaryExpression(cursor.left,
1588
								cursor.right, PLUS, cursor.arity);
1589
						cursor.arity = 0;
1590
						cursor.tuneArityMax();
1591
					}
1592
					cursor.right = expr2;
1593
					cursor.sourceEnd = expr2.sourceEnd;
1594
					// BE_INSTRUMENTATION: neutralized in the released code					
1595
//					cursor.depthTracker = ((BinaryExpression)cursor.left).
1596
//						depthTracker + 1;
1597
					this.expressionStack[this.expressionPtr] = cursor;
1598
			} else if (expr1 instanceof BinaryExpression && 
1599
							// single out the a + b case
1600
						((expr1.bits & ASTNode.OperatorMASK) >> 
1601
							ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
1602
					this.expressionStack[this.expressionPtr] = 
1603
						new CombinedBinaryExpression(expr1, expr2, PLUS, 1);
1604
				} else {
1605
					this.expressionStack[this.expressionPtr] = 
1606
						new BinaryExpression(expr1, expr2, PLUS);
1545
				}
1607
				}
1546
			} else {
1547
				this.expressionStack[this.expressionPtr] = 
1548
					new BinaryExpression(
1549
						expr1, 
1550
						expr2, 
1551
						op);
1552
			}
1553
			break;
1608
			break;
1554
		case LESS :
1609
		case LESS :
1555
			this.intPtr--;
1610
			this.intPtr--;
Lines 1619-1624 Link Here
1619
	*/
1674
	*/
1620
	Expression expr1 = this.expressionStack[this.expressionPtr + 1];
1675
	Expression expr1 = this.expressionStack[this.expressionPtr + 1];
1621
	Expression expr2 = this.expressionStack[this.expressionPtr];
1676
	Expression expr2 = this.expressionStack[this.expressionPtr];
1677
	// Note: we do not attempt to promote BinaryExpression-s to 
1678
	//       IndexedBinaryExpression-s here since expr1 always holds a name
1622
	switch(op) {
1679
	switch(op) {
1623
		case OR_OR :
1680
		case OR_OR :
1624
			this.expressionStack[this.expressionPtr] = 
1681
			this.expressionStack[this.expressionPtr] = 
(-)compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java (+393 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jdt.internal.compiler.ast;
12
13
import org.eclipse.jdt.internal.compiler.ASTVisitor;
14
import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
15
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
16
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
17
import org.eclipse.jdt.internal.compiler.impl.Constant;
18
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
19
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
20
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
21
22
/**
23
 * CombinedBinaryExpression is an implementation of BinaryExpression that 
24
 * specifically attempts to mitigate the issues raised by expressions which
25
 * have a very deep leftmost branch. It does so by maintaining a table of 
26
 * direct references to its subexpressions, and implementing non-recursive 
27
 * variants of the most significant recursive algorithms of its ancestors.
28
 * The subexpressions table only holds intermediate binary expressions. Its
29
 * role is to provide the reversed navigation through the left relationship
30
 * of BinaryExpression to Expression. To cope with potentially very deep
31
 * left branches, an instance of CombinedBinaryExpression is created once in
32
 * a while, using variable thresholds held by {@link #arityMax}.
33
 * As a specific case, the topmost node of all binary expressions that are
34
 * deeper than one is a CombinedBinaryExpression, but it has no references
35
 * table.<br>
36
 * Notes:
37
 * <ul>
38
 * <li>CombinedBinaryExpression is not meant to behave in other ways than 
39
 *     BinaryExpression in any observable respect;</li>
40
 * <li>visitors that implement their own traversal upon binary expressions
41
 *     should consider taking advantage of combined binary expressions, or
42
 *     else face a risk of StackOverflowError upon deep instances;</li>
43
 * <li>callers that need to change the operator should rebuild the expression
44
 *     from scratch, or else amend the references table as needed to cope with
45
 *     the resulting, separated expressions.</li>
46
 * </ul>
47
 */
48
public class CombinedBinaryExpression extends BinaryExpression {
49
50
	/** 
51
	 * The number of consecutive binary expressions of this' left branch that 
52
	 * bear the same operator as this.<br>
53
	 * Notes: 
54
	 * <ul><li>the presence of a CombinedBinaryExpression instance resets 
55
	 *         arity, even when its operator is compatible;</li>
56
	 *	   <li>this property is maintained by the parser.</li>
57
	 * </ul>
58
	 */
59
	public int arity;
60
	
61
	/**
62
	 * The threshold that will trigger the creation of the next full-fledged
63
	 * CombinedBinaryExpression. This field is only maintained for the
64
	 * topmost binary expression (it is 0 otherwise). It enables a variable
65
	 * policy, which scales better with very large expressions.
66
	 */
67
	public int arityMax;
68
	
69
	/**
70
	 * Upper limit for {@link #arityMax}.
71
	 */
72
	public static final int ARITY_MAX_MAX = 160;
73
	
74
	/**
75
	 * Default lower limit for {@link #arityMax}.
76
	 */
77
	public static final int ARITY_MAX_MIN = 20;
78
	
79
	/**
80
	 * Default value for the first term of the series of {@link #arityMax}
81
	 * values. Changing this allows for experimentation. Not meant to be 
82
	 * changed during a parse operation.
83
	 */
84
	public static int defaultArityMaxStartingValue = ARITY_MAX_MIN;
85
	
86
	/**
87
	 * A table of references to the binary expressions of this' left branch.
88
	 * Instances of CombinedBinaryExpression are not repeated here. Instead,
89
	 * the left subexpression of referencesTable[0] may be a combined binary
90
	 * expression, if appropriate. Null when this only cares about tracking 
91
	 * the expression's arity. 
92
	 */
93
	public BinaryExpression referencesTable[];
94
95
/**
96
 * Make a new CombinedBinaryExpression. If arity is strictly greater than one,
97
 * a references table is built and initialized with the reverse relationship of
98
 * the one defined by {@link BinaryExpression#left}. arity and left must be
99
 * compatible with each other (that is, there must be at least arity - 1
100
 * consecutive compatible binary expressions into the leftmost branch of left,
101
 * the topmost of which being left's immediate left expression).
102
 * @param left the left branch expression
103
 * @param right the right branch expression
104
 * @param operator the operator for this binary expression - only PLUS for now
105
 * @param arity the number of binary expressions of a compatible operator that
106
 *        already exist into the leftmost branch of left (including left); must
107
 *        be strictly greater than 0
108
 */
109
public CombinedBinaryExpression(Expression left, Expression right, int operator,
110
		int arity) {
111
	super(left, right, operator);
112
	this.arity = arity;
113
	if (arity > 1) {
114
		this.referencesTable = new BinaryExpression[arity];
115
		this.referencesTable[arity - 1] = (BinaryExpression) left;
116
		for (int i = arity - 1; i > 0; i--) {
117
			this.referencesTable[i - 1] = 
118
				(BinaryExpression) this.referencesTable[i].left; 
119
		}
120
	} else {
121
		this.arityMax = defaultArityMaxStartingValue;
122
	}
123
}
124
125
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, 
126
		FlowInfo flowInfo) {
127
	// keep implementation in sync with BinaryExpression#analyseCode	
128
	if (this.referencesTable == null) {
129
		return super.analyseCode(currentScope, flowContext, flowInfo);
130
	}
131
	BinaryExpression cursor;
132
	if ((cursor = this.referencesTable[0]).resolvedType.id != 
133
			TypeIds.T_JavaLangString) {
134
		cursor.left.checkNPE(currentScope, flowContext, flowInfo);
135
	}
136
	flowInfo = cursor.left.analyseCode(currentScope, flowContext, flowInfo).
137
		unconditionalInits();	
138
	for (int i = 0, end = this.arity; i < end; i ++) {
139
		if ((cursor = this.referencesTable[i]).resolvedType.id != 
140
				TypeIds.T_JavaLangString) {
141
			cursor.right.checkNPE(currentScope, flowContext, flowInfo);
142
		}
143
		flowInfo = cursor.right.
144
			analyseCode(currentScope, flowContext, flowInfo).
145
				unconditionalInits();
146
	}
147
	if (this.resolvedType.id != TypeIds.T_JavaLangString) {
148
		this.right.checkNPE(currentScope, flowContext, flowInfo);
149
	}
150
	return this.right.analyseCode(currentScope, flowContext, flowInfo).
151
		unconditionalInits();
152
}
153
154
public void generateOptimizedStringConcatenation(BlockScope blockScope, 
155
		CodeStream codeStream, int typeID) {
156
	// keep implementation in sync with BinaryExpression and Expression
157
	// #generateOptimizedStringConcatenation
158
	if (this.referencesTable == null) {
159
		super.generateOptimizedStringConcatenation(blockScope, codeStream, 
160
			typeID);
161
	} else {	
162
		if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == 
163
				OperatorIds.PLUS)
164
			&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
165
			if (this.constant != Constant.NotAConstant) {
166
				codeStream.generateConstant(this.constant, this.implicitConversion);
167
				codeStream.invokeStringConcatenationAppendForType(
168
						this.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
169
			} else {
170
				BinaryExpression cursor = this.referencesTable[0];
171
		
172
				int restart = 0;
173
	//			int cursorTypeID;
174
				int pc = codeStream.position;
175
				for (restart = this.arity - 1; restart >= 0; restart--) {
176
					if ((cursor = this.referencesTable[restart]).constant != 
177
							Constant.NotAConstant) {
178
						codeStream.generateConstant(cursor.constant, 
179
							cursor.implicitConversion);
180
						codeStream.invokeStringConcatenationAppendForType(
181
							cursor.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
182
						break;
183
					}
184
					// never happens for now - may reconsider if we decide to 
185
					// cover more than string concatenation
186
	//				if (!((((cursor = this.referencesTable[restart]).bits & 
187
	//						ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == 
188
	//							OperatorIds.PLUS) & 
189
	//						((cursorTypeID = cursor.bits & ASTNode.ReturnTypeIDMASK) == 
190
	//							TypeIds.T_JavaLangString)) {
191
	//					if (cursorTypeID == T_JavaLangString && 
192
	//							cursor.constant != Constant.NotAConstant && 
193
	//							cursor.constant.stringValue().length() == 0) {
194
	//						break; // optimize str + ""
195
	//					}
196
	//					cursor.generateCode(blockScope, codeStream, true);
197
	//					codeStream.invokeStringConcatenationAppendForType(
198
	//							cursorTypeID);					
199
	//					break;
200
	//				}
201
				}
202
				restart++;
203
				if (restart == 0) { // reached the leftmost expression
204
					cursor.left.generateOptimizedStringConcatenation(
205
						blockScope,
206
						codeStream,
207
						cursor.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
208
				}
209
				int pcAux;
210
				for (int i = restart; i < this.arity; i++) {
211
					codeStream.recordPositionsFrom(pc, 
212
						(cursor = this.referencesTable[i]).left.sourceStart);
213
					pcAux = codeStream.position;
214
					cursor.right.generateOptimizedStringConcatenation(blockScope, 
215
						codeStream,	cursor.right.implicitConversion & 
216
							TypeIds.COMPILE_TYPE_MASK);
217
					codeStream.recordPositionsFrom(pcAux, cursor.right.sourceStart);
218
				}
219
				codeStream.recordPositionsFrom(pc, this.left.sourceStart);
220
				pc = codeStream.position;
221
				this.right.generateOptimizedStringConcatenation(
222
					blockScope,
223
					codeStream,
224
					this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
225
				codeStream.recordPositionsFrom(pc, this.right.sourceStart);
226
			}
227
		} else {
228
			super.generateOptimizedStringConcatenation(blockScope, codeStream, 
229
				typeID);
230
		}
231
	}
232
}
233
234
public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, 
235
		CodeStream codeStream, int typeID) {
236
	// keep implementation in sync with BinaryExpression
237
	// #generateOptimizedStringConcatenationCreation
238
	if (this.referencesTable == null) {
239
		super.generateOptimizedStringConcatenationCreation(blockScope, 
240
			codeStream,	typeID);
241
	} else {
242
		if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == 
243
				OperatorIds.PLUS) && 
244
					((this.bits & ASTNode.ReturnTypeIDMASK) == 
245
						TypeIds.T_JavaLangString) && 
246
					this.constant == Constant.NotAConstant) {
247
			int pc = codeStream.position;
248
			BinaryExpression cursor = this.referencesTable[this.arity - 1];
249
				// silence warnings
250
			int restart = 0;
251
			for (restart = this.arity - 1; restart >= 0; restart--) {
252
				if (((((cursor = this.referencesTable[restart]).bits & 
253
						ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == 
254
							OperatorIds.PLUS) && 
255
						((cursor.bits & ASTNode.ReturnTypeIDMASK) == 
256
							TypeIds.T_JavaLangString)) {
257
					if (cursor.constant != Constant.NotAConstant) {
258
						codeStream.newStringContatenation(); // new: java.lang.StringBuffer
259
						codeStream.dup();
260
						codeStream.ldc(cursor.constant.stringValue());
261
						codeStream.invokeStringConcatenationStringConstructor();
262
						// invokespecial: java.lang.StringBuffer.<init>(Ljava.lang.String;)V
263
						break;
264
					}
265
				} else {
266
					cursor.generateOptimizedStringConcatenationCreation(blockScope, 
267
						codeStream, cursor.implicitConversion & 
268
							TypeIds.COMPILE_TYPE_MASK);
269
					break;
270
				}
271
			}
272
			restart++;
273
			if (restart == 0) { // reached the leftmost expression
274
				cursor.left.generateOptimizedStringConcatenationCreation(
275
					blockScope,
276
					codeStream,
277
					this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
278
			}
279
			int pcAux;
280
			for (int i = restart; i < this.arity; i++) {
281
				codeStream.recordPositionsFrom(pc, 
282
					(cursor = this.referencesTable[i]).left.sourceStart);
283
				pcAux = codeStream.position;
284
				cursor.right.generateOptimizedStringConcatenation(blockScope, 
285
					codeStream,	cursor.right.implicitConversion & 
286
						TypeIds.COMPILE_TYPE_MASK);
287
				codeStream.recordPositionsFrom(pcAux, cursor.right.sourceStart);
288
			}
289
			codeStream.recordPositionsFrom(pc, this.left.sourceStart);
290
			pc = codeStream.position;
291
			this.right.generateOptimizedStringConcatenation(
292
				blockScope,
293
				codeStream,
294
				this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
295
			codeStream.recordPositionsFrom(pc, this.right.sourceStart);
296
		} else {
297
			super.generateOptimizedStringConcatenationCreation(blockScope, 
298
				codeStream, typeID);
299
		}
300
	}
301
}
302
303
public StringBuffer printExpressionNoParenthesis(int indent, 
304
		StringBuffer output) {
305
	// keep implementation in sync with 
306
	// BinaryExpression#printExpressionNoParenthesis and 
307
	// OperatorExpression#printExpression
308
	if (this.referencesTable == null) {
309
		return super.printExpressionNoParenthesis(indent, output);
310
	}
311
	String operatorString = operatorToString();
312
	for (int i = this.arity - 1; i >= 0; i--) {
313
		output.append('(');
314
	}
315
	output = this.referencesTable[0].left.
316
		printExpression(indent, output);
317
	for (int i = 0, end = this.arity; 
318
				i < end; i++) {
319
		output.append(' ').append(operatorString).append(' ');
320
		output = this.referencesTable[i].right.
321
			printExpression(0, output);
322
		output.append(')');
323
	}
324
	output.append(' ').append(operatorString).append(' ');
325
	return this.right.printExpression(0, output);
326
}
327
328
public TypeBinding resolveType(BlockScope scope) {
329
	// keep implementation in sync with BinaryExpression#resolveType
330
	if (this.referencesTable == null) {
331
		return super.resolveType(scope);
332
	}
333
	BinaryExpression cursor;
334
	if ((cursor = this.referencesTable[0]).left instanceof CastExpression) {
335
		cursor.left.bits |= ASTNode.DisableUnnecessaryCastCheck; 
336
			// will check later on
337
	}
338
	cursor.left.resolveType(scope);
339
	for (int i = 0, end = this.arity; i < end; i ++) {
340
		this.referencesTable[i].nonRecursiveResolveTypeUpwards(scope);
341
	}
342
	nonRecursiveResolveTypeUpwards(scope);
343
	return this.resolvedType;
344
}
345
346
public void traverse(ASTVisitor visitor, BlockScope scope) {
347
	if (this.referencesTable == null) {
348
		super.traverse(visitor, scope);
349
	} else {
350
		if (visitor.visit(this, scope)) {
351
			int restart;
352
			for (restart = this.arity - 1; 
353
					restart >= 0; 
354
					restart--) {
355
				if (!visitor.visit(
356
						this.referencesTable[restart], scope)) {
357
					visitor.endVisit(
358
						this.referencesTable[restart], scope);
359
					break;
360
				}
361
			}
362
			restart++;
363
			// restart now points to the deepest BE for which
364
			// visit returned true, if any
365
			if (restart == 0) {
366
				this.referencesTable[0].left.traverse(visitor, scope);
367
			}
368
			for (int i = restart, end = this.arity; 
369
						i < end; i++) {
370
				this.referencesTable[i].right.traverse(visitor, scope);
371
				visitor.endVisit(this.referencesTable[i], scope);
372
			}
373
			this.right.traverse(visitor, scope);
374
		}
375
		visitor.endVisit(this, scope);
376
	}
377
}
378
379
/**
380
 * Change {@link #arityMax} if and as needed. The current policy is to double 
381
 * arityMax each time this method is called, until it reaches 
382
 * {@link #ARITY_MAX_MAX}. Other policies may consider incrementing it less
383
 * agressively. Call only after an appropriate value has been assigned to 
384
 * {@link #left}.
385
 */
386
// more sophisticate increment policies would leverage the leftmost expression
387
// to hold an indication of the number of uses of a given arityMax in a row
388
public void tuneArityMax() {
389
	if (this.arityMax < ARITY_MAX_MAX) {
390
		this.arityMax *= 2;
391
	}
392
}
393
}
(-)src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java (+25 lines)
Lines 752-757 Link Here
752
		"The local variable s may not have been initialized\n" + 
752
		"The local variable s may not have been initialized\n" + 
753
		"----------\n");
753
		"----------\n");
754
}
754
}
755
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
756
// Non-recursive approach for deep binary expressions. Check that the
757
// flow analysis doesn't break.
758
public void test027() {
759
	this.runConformTest(
760
		new String[] {
761
			"X.java",
762
			"public class X {\n" + 
763
			"  public static void main(String args[]) {\n" + 
764
			"    String s;\n" + 
765
			"    if (args.length == 0) {\n" + 
766
			"      s = \"s\";\n" + 
767
			"    } else {\n" + 
768
			"      s = args[0];\n" + 
769
			"    }\n" + 
770
			"    System.out.println(s + \"-\" + s + \"-\" + s + \"-\" +\n" +
771
			"                       s + \"-\" + s + \"-\" + s + \"-\" +\n" + 
772
			"                       s + \"-\" + s + \"-\" + s + \"-\" +\n" + 
773
			"                       s + \"-\" + s + \"-\" + s + \"-\" +\n" + 
774
			"                       s + \"-\" + s + \"-\" + s + \"-\");\n" + 
775
			"  }\n" + 
776
			"}"
777
		},
778
		"s-s-s-s-s-s-s-s-s-s-s-s-s-s-s-");
779
}
755
public static Class testClass() {
780
public static Class testClass() {
756
	return FlowAnalysisTest.class;
781
	return FlowAnalysisTest.class;
757
}
782
}
(-)src/org/eclipse/jdt/core/tests/compiler/regression/XLargeTest.java (+93 lines)
Lines 11-16 Link Here
11
package org.eclipse.jdt.core.tests.compiler.regression;
11
package org.eclipse.jdt.core.tests.compiler.regression;
12
12
13
import java.util.Map;
13
import java.util.Map;
14
import java.util.Random;
14
15
15
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
16
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
16
17
Lines 3581-3586 Link Here
3581
		},
3582
		},
3582
		"true");
3583
		"true");
3583
}
3584
}
3585
3586
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
3587
// Failed before using a non recursive implementation of deep binary
3588
// expressions.
3589
public void test010() {
3590
	StringBuffer sourceCode = new StringBuffer(			
3591
			"public class X {\n" + 
3592
			"  void foo(String a, String b, String c, String d, String e) {\n" +
3593
			"    String s = \n");
3594
	for (int i = 0; i < 350; i++) {
3595
		sourceCode.append(
3596
			"    	\"abcdef\" + a + b + c + d + e + " +
3597
			"\" ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno" +
3598
			"pqrstuvwxyzabcdefghijklmnopqrstuvwxyz\" +\n");
3599
	}
3600
	sourceCode.append(			
3601
			"    	\"abcdef\" + a + b + c + d + e + \" ghijklmnopqrstuvwxyz" +
3602
			"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
3603
			"abcdefghijklmnopqrstuvwxy12\";\n" + 
3604
			"    }\n" + 
3605
			"}");
3606
	this.runConformTest(
3607
		new String[] {
3608
			"X.java",
3609
			sourceCode.toString()
3610
		},
3611
		"");
3612
}
3613
3614
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
3615
// check if we hit the 64Kb limit on method code lenth in class files before 
3616
// filling the stack
3617
// need to use a computed string (else this source file will get blown away
3618
// as well)
3619
public void test011() {
3620
	int length = 3 * 54 * 1000; 
3621
		// the longer the slower, but still needs to reach the limit...
3622
	StringBuffer veryLongString = new StringBuffer(length + 20);
3623
	veryLongString.append('"');
3624
	Random random = new Random();
3625
	while (veryLongString.length() < length) {
3626
		veryLongString.append("\"+a+\"");
3627
		veryLongString.append(random.nextLong());
3628
	}
3629
	veryLongString.append('"');
3630
	this.runNegativeTest(
3631
		new String[] {
3632
			"X.java",
3633
			"public class X {\n" + 
3634
			"  void foo(String a, String b, String c, String d, String e) {\n" +
3635
			"    String s = \n" +
3636
			veryLongString.toString() +
3637
			"    	+ \"abcdef\" + a + b + c + d + e + \" ghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxy12\";\n" + 
3638
			"    }\n" + 
3639
			"}"
3640
		},
3641
		"----------\n" +
3642
		"1. ERROR in X.java (at line 2)\n" +
3643
		"	void foo(String a, String b, String c, String d, String e) {\n" +
3644
		"	     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
3645
		"The code of method foo(String, String, String, String, String) is " +
3646
			"exceeding the 65535 bytes limit\n" +
3647
		"----------\n");
3648
}
3649
3650
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
3651
// variant: right member of the topmost expression is left-deep
3652
public void test012() {
3653
	StringBuffer sourceCode = new StringBuffer(			
3654
			"public class X {\n" + 
3655
			"  void foo(String a, String b, String c, String d, String e) {\n" +
3656
			"    String s = a + (\n");
3657
	for (int i = 0; i < 1000; i++) {
3658
		sourceCode.append(
3659
			"    	\"abcdef\" + a + b + c + d + e + " +
3660
			"\" ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno" +
3661
			"pqrstuvwxyzabcdefghijklmnopqrstuvwxyz\" +\n");
3662
	}
3663
	sourceCode.append(			
3664
			"    	\"abcdef\" + a + b + c + d + e + \" ghijklmnopqrstuvwxyz" +
3665
			"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
3666
			"abcdefghijklmnopqrstuvwxy12\");\n" + 
3667
			"    }\n" + 
3668
			"}");
3669
	this.runConformTest(
3670
		new String[] {
3671
			"X.java",
3672
			sourceCode.toString()
3673
		},
3674
		"");
3675
}
3676
3584
public static Class testClass() {
3677
public static Class testClass() {
3585
	return XLargeTest.class;
3678
	return XLargeTest.class;
3586
}
3679
}
(-)src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java (+1 lines)
Lines 31-36 Link Here
31
//	standardTests.addAll(JavadocTest.allTestClasses);
31
//	standardTests.addAll(JavadocTest.allTestClasses);
32
	standardTests.add(ArrayTest.class);
32
	standardTests.add(ArrayTest.class);
33
	standardTests.add(AssignmentTest.class);
33
	standardTests.add(AssignmentTest.class);
34
	standardTests.add(ASTImplTests.class);
34
	standardTests.add(BooleanTest.class);
35
	standardTests.add(BooleanTest.class);
35
	standardTests.add(CastTest.class);
36
	standardTests.add(CastTest.class);
36
	standardTests.add(ClassFileComparatorTest.class);
37
	standardTests.add(ClassFileComparatorTest.class);
(-)src/org/eclipse/jdt/core/tests/compiler/regression/ASTImplTests.java (+827 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2006 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jdt.core.tests.compiler.regression;
12
13
import junit.framework.Test;
14
15
import org.eclipse.jdt.core.tests.util.Util;
16
import org.eclipse.jdt.internal.compiler.ASTVisitor;
17
import org.eclipse.jdt.internal.compiler.CompilationResult;
18
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
19
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
20
import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
21
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
22
import org.eclipse.jdt.internal.compiler.ast.CombinedBinaryExpression;
23
import org.eclipse.jdt.internal.compiler.ast.ExtendedStringLiteral;
24
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
25
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
26
import org.eclipse.jdt.internal.compiler.ast.StringLiteralConcatenation;
27
import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;
28
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
29
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
30
import org.eclipse.jdt.internal.compiler.parser.Parser;
31
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
32
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
33
34
/**
35
 * A tests series especially meant to validate the internals of our AST
36
 * implementation.
37
 */
38
public class ASTImplTests extends AbstractRegressionTest {
39
public ASTImplTests(String name) { 
40
    super(name);
41
}
42
43
	// Static initializer to specify tests subset using TESTS_* static variables
44
  	// All specified tests which does not belong to the class are skipped...
45
  	// Only the highest compliance level is run; add the VM argument
46
  	// -Dcompliance=1.4 (for example) to lower it if needed
47
  	static {
48
//    	TESTS_NAMES = new String[] { "test2050" };
49
//    	TESTS_NUMBERS = new int[] { 3 };   
50
//    	TESTS_NUMBERS = new int[] { 2999 };   
51
//    	TESTS_RANGE = new int[] { 2050, -1 }; 
52
  	}
53
54
public static Test suite() {
55
    return buildAllCompliancesTestSuite(testClass());
56
}
57
  
58
public static Class testClass() {
59
    return ASTImplTests.class;
60
}
61
62
// Helper methods
63
static Parser defaultParser = new Parser(
64
			new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), 
65
			new CompilerOptions(), 
66
			new DefaultProblemFactory()), false);
67
public void runConformTest(String fileName, String fileContents, 
68
		Parser parser, ASTCollector visitor, String expected) {
69
	CompilationUnit source = 
70
		new CompilationUnit(fileContents.toCharArray(),	fileName, null);
71
	CompilationResult compilationResult =
72
		new CompilationResult(source, 1, 1, 10); 
73
	CompilationUnitDeclaration unit = parser.parse(source, compilationResult);
74
	assertEquals(0, compilationResult.problemCount);
75
	unit.traverse(visitor, unit.scope);
76
	String result = visitor.result();
77
	if (! expected.equals(result)) {
78
		System.out.println(getClass().getName() + '#' + getName());
79
		System.out.println("Expected:");
80
		System.out.println(expected);
81
		System.out.println("But was:");
82
		System.out.println(result);
83
		System.out.println("Cut and paste:");
84
		System.out.println(Util.displayString(result, INDENT, SHIFT));
85
	}
86
	assertEquals(expected, result);
87
}
88
89
// AST implementation - visiting binary expressions
90
public void test0001_regular_binary_expression() {
91
	runConformTest(
92
		"X.java", 
93
		"public class X {\n" + 
94
		"  void foo() {\n" + 
95
		"    String s1 = \"s1\";\n" + 
96
		"    String s2 = \"s2\";\n" + 
97
		"    String s3 = \"s3\";\n" + 
98
		"    String s4 = \"s4\";\n" + 
99
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
100
		"      s3 + \"l3\" + s4);\n" + 
101
		"  }\n" + 
102
		"}\n",
103
		defaultParser,
104
		new ASTBinaryExpressionCollector(),
105
		"[v SL \"s1\"]\n" + 
106
		"[ev SL \"s1\"]\n" + 
107
		"[v SL \"s2\"]\n" + 
108
		"[ev SL \"s2\"]\n" + 
109
		"[v SL \"s3\"]\n" + 
110
		"[ev SL \"s3\"]\n" + 
111
		"[v SL \"s4\"]\n" + 
112
		"[ev SL \"s4\"]\n" + 
113
		"[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + 
114
		"[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
115
		"[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
116
		"[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
117
		"[v BE ((s1 + \"l1\") + s2)]\n" + 
118
		"[v BE (s1 + \"l1\")]\n" + 
119
		"[v SNR s1]\n" + 
120
		"[ev SNR s1]\n" + 
121
		"[v SL \"l1\"]\n" + 
122
		"[ev SL \"l1\"]\n" + 
123
		"[ev BE (s1 + \"l1\")]\n" + 
124
		"[v SNR s2]\n" + 
125
		"[ev SNR s2]\n" + 
126
		"[ev BE ((s1 + \"l1\") + s2)]\n" + 
127
		"[v SL \"l2\"]\n" + 
128
		"[ev SL \"l2\"]\n" + 
129
		"[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
130
		"[v SNR s3]\n" + 
131
		"[ev SNR s3]\n" + 
132
		"[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
133
		"[v SL \"l3\"]\n" + 
134
		"[ev SL \"l3\"]\n" + 
135
		"[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
136
		"[v SNR s4]\n" + 
137
		"[ev SNR s4]\n" + 
138
		"[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n");
139
}
140
141
// AST implementation - visiting binary expressions
142
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
143
// Adding combined binary expressions
144
public void test0002_combined_binary_expression() {
145
	CombinedBinaryExpression.defaultArityMaxStartingValue = 3;
146
	// one CBE each fourth BE
147
	runConformTest(
148
		"X.java", 
149
		"public class X {\n" + 
150
		"  void foo() {\n" + 
151
		"    String s1 = \"s1\";\n" + 
152
		"    String s2 = \"s2\";\n" + 
153
		"    String s3 = \"s3\";\n" + 
154
		"    String s4 = \"s4\";\n" + 
155
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
156
		"      s3 + \"l3\" + s4);\n" + 
157
		"  }\n" + 
158
		"}\n",
159
		defaultParser,
160
		new ASTBinaryExpressionCollector() {
161
			public void endVisit(BinaryExpression binaryExpression, BlockScope scope) {
162
				if (binaryExpression instanceof CombinedBinaryExpression &&
163
						((CombinedBinaryExpression) binaryExpression).
164
							referencesTable != null) {
165
					this.collector.append("[ev CBE " + 
166
						cut(binaryExpression.toString()) + "]\n");
167
				} else {
168
					super.endVisit(binaryExpression, scope);
169
				}
170
			}
171
		},
172
		"[v SL \"s1\"]\n" + 
173
		"[ev SL \"s1\"]\n" + 
174
		"[v SL \"s2\"]\n" + 
175
		"[ev SL \"s2\"]\n" + 
176
		"[v SL \"s3\"]\n" + 
177
		"[ev SL \"s3\"]\n" + 
178
		"[v SL \"s4\"]\n" + 
179
		"[ev SL \"s4\"]\n" + 
180
		"[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + 
181
		"[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
182
		"[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
183
		"[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
184
		"[v BE ((s1 + \"l1\") + s2)]\n" + 
185
		"[v BE (s1 + \"l1\")]\n" + 
186
		"[v SNR s1]\n" + 
187
		"[ev SNR s1]\n" + 
188
		"[v SL \"l1\"]\n" + 
189
		"[ev SL \"l1\"]\n" + 
190
		"[ev BE (s1 + \"l1\")]\n" + 
191
		"[v SNR s2]\n" + 
192
		"[ev SNR s2]\n" + 
193
		"[ev BE ((s1 + \"l1\") + s2)]\n" + 
194
		"[v SL \"l2\"]\n" + 
195
		"[ev SL \"l2\"]\n" + 
196
		"[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
197
		"[v SNR s3]\n" + 
198
		"[ev SNR s3]\n" + 
199
		"[ev CBE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
200
		"[v SL \"l3\"]\n" + 
201
		"[ev SL \"l3\"]\n" + 
202
		"[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
203
		"[v SNR s4]\n" + 
204
		"[ev SNR s4]\n" + 
205
		"[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n");
206
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
207
		CombinedBinaryExpression.ARITY_MAX_MIN;
208
}
209
210
// AST implementation - visiting binary expressions
211
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
212
// Adding combined binary expressions
213
public void test0003_combined_binary_expression() {
214
	Parser parser = new Parser(
215
			new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), 
216
			new CompilerOptions(), 
217
			new DefaultProblemFactory()), true); // optimize string literals
218
	CombinedBinaryExpression.defaultArityMaxStartingValue = 2;
219
		// one CBE each third BE - except the top one, which is degenerate (no
220
		// references table)
221
	runConformTest(
222
		"X.java", 
223
		"public class X {\n" + 
224
		"  void foo() {\n" + 
225
		"    String s1 = \"s1\";\n" + 
226
		"    String s2 = \"s2\";\n" + 
227
		"    String s3 = \"s3\";\n" + 
228
		"    String s4 = \"s4\";\n" + 
229
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
230
		"      s3 + \"l3\" + s4);\n" + 
231
		"  }\n" + 
232
		"}\n",
233
		parser,		
234
		new ASTBinaryExpressionCollector() {
235
			public void endVisit(BinaryExpression binaryExpression, BlockScope scope) {
236
				if (binaryExpression instanceof CombinedBinaryExpression &&
237
						((CombinedBinaryExpression) binaryExpression).
238
							referencesTable != null) {
239
					this.collector.append("[ev CBE " + 
240
						cut(binaryExpression.toString()) + "]\n");
241
				} else {
242
					super.endVisit(binaryExpression, scope);
243
				}
244
			}
245
		},
246
		"[v SL \"s1\"]\n" + 
247
		"[ev SL \"s1\"]\n" + 
248
		"[v SL \"s2\"]\n" + 
249
		"[ev SL \"s2\"]\n" + 
250
		"[v SL \"s3\"]\n" + 
251
		"[ev SL \"s3\"]\n" + 
252
		"[v SL \"s4\"]\n" + 
253
		"[ev SL \"s4\"]\n" + 
254
		"[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + 
255
		"[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
256
		"[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
257
		"[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
258
		"[v BE ((s1 + \"l1\") + s2)]\n" + 
259
		"[v BE (s1 + \"l1\")]\n" + 
260
		"[v SNR s1]\n" + 
261
		"[ev SNR s1]\n" + 
262
		"[v SL \"l1\"]\n" + 
263
		"[ev SL \"l1\"]\n" + 
264
		"[ev BE (s1 + \"l1\")]\n" + 
265
		"[v SNR s2]\n" + 
266
		"[ev SNR s2]\n" + 
267
		"[ev BE ((s1 + \"l1\") + s2)]\n" + 
268
		"[v SL \"l2\"]\n" + 
269
		"[ev SL \"l2\"]\n" + 
270
		"[ev CBE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
271
		"[v SNR s3]\n" + 
272
		"[ev SNR s3]\n" + 
273
		"[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
274
		"[v SL \"l3\"]\n" + 
275
		"[ev SL \"l3\"]\n" + 
276
		"[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + 
277
		"[v SNR s4]\n" + 
278
		"[ev SNR s4]\n" + 
279
		"[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n");
280
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
281
		CombinedBinaryExpression.ARITY_MAX_MIN;
282
}
283
284
// AST implementation - visiting binary expressions
285
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
286
// Adding combined binary expressions - effect of a literal at the start with
287
// string literal optimization
288
public void test0004_combined_binary_expression() {
289
	Parser parser = new Parser(
290
			new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), 
291
			new CompilerOptions(), 
292
			new DefaultProblemFactory()), true); // optimize string literals
293
	runConformTest(
294
		"X.java", 
295
		"public class X {\n" + 
296
		"  void foo() {\n" + 
297
		"    String s1 = \"s1\";\n" + 
298
		"    System.out.println(\"l\" + \"1\" + s1);\n" +
299
			// "l" + "1" is collapsed into "l1" without affecting binary 
300
			// expressions: only one BE
301
		"  }\n" + 
302
		"}\n",
303
		parser,
304
		new ASTBinaryExpressionCollector(),
305
		"[v SL \"s1\"]\n" + 
306
		"[ev SL \"s1\"]\n" + 
307
		"[v BE (ExtendedStringLiter...} + s1)]\n" + 
308
		"[v ESL ExtendedStringLiteral{l1}]\n" + 
309
		"[ev ESL ExtendedStringLiteral{l1}]\n" + 
310
		"[v SNR s1]\n" + 
311
		"[ev SNR s1]\n" + 
312
		"[ev BE (ExtendedStringLiter...} + s1)]\n");
313
}
314
315
// AST implementation - visiting binary expressions
316
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
317
// Adding combined binary expressions - effect of a literal at the start without
318
// string literals optimization
319
public void test0005_combined_binary_expression() {
320
	runConformTest(
321
		"X.java", 
322
		"public class X {\n" + 
323
		"  void foo() {\n" + 
324
		"    String s1 = \"s1\";\n" + 
325
		"    System.out.println(\"l\" + \"1\" + s1);\n" +
326
			// "l" + "1" is handled by a string literal concatenation without 
327
			// affecting binary expressions: only one BE
328
		"  }\n" + 
329
		"}\n",
330
		defaultParser,
331
		new ASTBinaryExpressionCollector(),
332
		"[v SL \"s1\"]\n" + 
333
		"[ev SL \"s1\"]\n" + 
334
		"[v BE (StringLiteralConcat...} + s1)]\n" + 
335
		"[v SLC StringLiteralConcate...\n" + 
336
		"\"1\"+\n" + 
337
		"}]\n" + 
338
		"[v SL \"l\"]\n" + 
339
		"[ev SL \"l\"]\n" + 
340
		"[v SL \"1\"]\n" + 
341
		"[ev SL \"1\"]\n" + 
342
		"[ev SLC StringLiteralConcate...\n" + 
343
		"\"1\"+\n" + 
344
		"}]\n" + 
345
		"[v SNR s1]\n" + 
346
		"[ev SNR s1]\n" + 
347
		"[ev BE (StringLiteralConcat...} + s1)]\n");
348
}
349
350
// AST implementation - visiting binary expressions
351
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
352
// Adding combined binary expressions - cutting the traversal half-way down
353
public void test0006_combined_binary_expression() {
354
	CombinedBinaryExpression.defaultArityMaxStartingValue = 1;
355
	runConformTest(
356
		"X.java", 
357
		"public class X {\n" + 
358
		"  void foo() {\n" + 
359
		"    String s1 = \"s1\";\n" + 
360
		"    String s2 = \"s2\";\n" + 
361
		"    String s3 = \"s3\";\n" + 
362
		"    String s4 = \"s4\";\n" + 
363
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
364
		"      s3 + s1 + s4);\n" + 
365
		"  }\n" + 
366
		"}\n",
367
		defaultParser,
368
		new ASTBinaryExpressionCollector() {
369
			public boolean visit(BinaryExpression binaryExpression, BlockScope scope) {
370
				super.visit(binaryExpression, scope);
371
				if (binaryExpression.right instanceof StringLiteral) {
372
					return false;
373
				}
374
				return true;
375
			}
376
		},
377
		"[v SL \"s1\"]\n" + 
378
		"[ev SL \"s1\"]\n" + 
379
		"[v SL \"s2\"]\n" + 
380
		"[ev SL \"s2\"]\n" + 
381
		"[v SL \"s3\"]\n" + 
382
		"[ev SL \"s3\"]\n" + 
383
		"[v SL \"s4\"]\n" + 
384
		"[ev SL \"s4\"]\n" + 
385
		"[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + 
386
		"[v BE (((((s1 + \"l1\") + s2...) + s1)]\n" + 
387
		"[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
388
		"[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
389
		"[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
390
		"[v SNR s3]\n" + 
391
		"[ev SNR s3]\n" + 
392
		"[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + 
393
		"[v SNR s1]\n" + 
394
		"[ev SNR s1]\n" + 
395
		"[ev BE (((((s1 + \"l1\") + s2...) + s1)]\n" + 
396
		"[v SNR s4]\n" + 
397
		"[ev SNR s4]\n" + 
398
		"[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n");
399
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
400
		CombinedBinaryExpression.ARITY_MAX_MIN;
401
}
402
403
// AST implementation - visiting binary expressions
404
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
405
// Adding combined binary expressions - cutting the traversal right away
406
public void test0007_combined_binary_expression() {
407
	CombinedBinaryExpression.defaultArityMaxStartingValue = 4; 
408
	runConformTest(
409
		"X.java", 
410
		"public class X {\n" + 
411
		"  void foo() {\n" + 
412
		"    String s1 = \"s1\";\n" + 
413
		"    String s2 = \"s2\";\n" + 
414
		"    String s3 = \"s3\";\n" + 
415
		"    String s4 = \"s4\";\n" + 
416
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
417
		"      s3 + \"l3\" + s4);\n" + 
418
		"  }\n" + 
419
		"}\n",
420
		defaultParser,
421
		new ASTBinaryExpressionCollector() {
422
			public boolean visit(BinaryExpression binaryExpression, BlockScope scope) {
423
				super.visit(binaryExpression, scope);
424
				return false;
425
			}
426
		},
427
		"[v SL \"s1\"]\n" + 
428
		"[ev SL \"s1\"]\n" + 
429
		"[v SL \"s2\"]\n" + 
430
		"[ev SL \"s2\"]\n" + 
431
		"[v SL \"s3\"]\n" + 
432
		"[ev SL \"s3\"]\n" + 
433
		"[v SL \"s4\"]\n" + 
434
		"[ev SL \"s4\"]\n" + 
435
		"[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + 
436
		"[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n");
437
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
438
		CombinedBinaryExpression.ARITY_MAX_MIN;
439
}
440
441
// AST implementation - visiting binary expressions
442
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
443
// Adding combined binary expressions - case of one-deep expression
444
public void test0008_combined_binary_expression() {
445
	runConformTest(
446
		"X.java", 
447
		"public class X {\n" + 
448
		"  void foo() {\n" + 
449
		"    String s1 = \"s1\";\n" + 
450
		"    String s2 = \"s2\";\n" + 
451
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\");\n" + 
452
		"    System.out.println(s1 + s2);\n" + 
453
		"  }\n" + 
454
		"}\n",
455
		defaultParser,		
456
		new ASTBinaryExpressionCollector() {
457
			public void endVisit(BinaryExpression binaryExpression, BlockScope scope) {
458
				if (binaryExpression instanceof CombinedBinaryExpression) {
459
					this.collector.append("[ev CBE " + 
460
						cut(binaryExpression.toString()) + "]\n");
461
				} else {
462
					super.endVisit(binaryExpression, scope);
463
				}
464
			}
465
		},
466
		"[v SL \"s1\"]\n" + 
467
		"[ev SL \"s1\"]\n" + 
468
		"[v SL \"s2\"]\n" + 
469
		"[ev SL \"s2\"]\n" + 
470
		"[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
471
		"[v BE ((s1 + \"l1\") + s2)]\n" + 
472
		"[v BE (s1 + \"l1\")]\n" + 
473
		"[v SNR s1]\n" + 
474
		"[ev SNR s1]\n" + 
475
		"[v SL \"l1\"]\n" + 
476
		"[ev SL \"l1\"]\n" + 
477
		"[ev BE (s1 + \"l1\")]\n" + 
478
		"[v SNR s2]\n" + 
479
		"[ev SNR s2]\n" + 
480
		"[ev BE ((s1 + \"l1\") + s2)]\n" + 
481
		"[v SL \"l2\"]\n" + 
482
		"[ev SL \"l2\"]\n" + 
483
		"[ev CBE (((s1 + \"l1\") + s2) + \"l2\")]\n" + 
484
		"[v BE (s1 + s2)]\n" + 
485
		"[v SNR s1]\n" + 
486
		"[ev SNR s1]\n" + 
487
		"[v SNR s2]\n" + 
488
		"[ev SNR s2]\n" + 
489
		"[ev BE (s1 + s2)]\n");
490
}
491
492
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
493
// check if the generated code is OK when leveraging CombinedBinaryExpression
494
public void test0009_combined_binary_expression() {
495
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
496
	this.runConformTest(
497
		new String[] {
498
			"X.java",
499
			"public class X {\n" + 
500
			"public static void main(String args[]) {\n" + 
501
			"    final int max = 30; \n" + 
502
			"    String s[] = new String[max];\n" + 
503
			"    for (int i = 0; i < max; i++) {\n" + 
504
			"        s[i] = \"a\";\n" + 
505
			"    }\n" + 
506
			"    foo(s);\n" + 
507
			"}\n" + 
508
			"static void foo (String s[]) {\n" + 
509
			"    System.out.println(\n" + 
510
			"        s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + \n" +
511
			"        s[7] + s[8] + s[9] + s[10] + s[11] + s[12] + s[13] +\n" +
512
			"        s[14] + s[15] + s[16] + s[17] + s[18] + s[19] + \n" + 
513
			"        s[20] + s[21] + s[22] + s[23] + s[24] + s[25] + \n" +
514
			"        s[26] + s[27] + s[28] + s[29]\n" + 
515
			"        );\n" + 
516
			"}\n" + 
517
			"}"},
518
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
519
}
520
521
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
522
// check if the generated code is OK when leveraging CombinedBinaryExpression
523
// variant involving constant binary expressions deep in the tree
524
public void test0010_combined_binary_expression() {
525
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
526
	this.runConformTest(
527
		new String[] {
528
			"X.java",
529
			"public class X {\n" + 
530
			"public static void main(String args[]) {\n" + 
531
			"    final int max = 30; \n" + 
532
			"    String s[] = new String[max];\n" + 
533
			"    for (int i = 0; i < max; i++) {\n" + 
534
			"        s[i] = \"a\";\n" + 
535
			"    }\n" + 
536
			"    foo(s);\n" + 
537
			"}\n" + 
538
			"static void foo (String s[]) {\n" +
539
			"    final String c = \"a\";" + 
540
			"    System.out.println(\n" + 
541
			"        c + c + c + c + s[4] + s[5] + s[6] + s[7] + s[8] + \n" +
542
			"        s[9] + s[10] + s[11] + s[12] + s[13] + s[14] + \n" +
543
			"        s[15] + s[16] + s[17] + s[18] + s[19] + s[20] + \n" +
544
			"        s[21] + s[22] + s[23] + s[24] + s[25] + s[26] + \n" +
545
			"        s[27] + s[28] + s[29]\n" + 
546
			"        );\n" + 
547
			"}\n" + 
548
			"}"
549
		},
550
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
551
}
552
553
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
554
// check if the generated code is OK when leveraging CombinedBinaryExpression
555
// variant involving a constant combined binary expression
556
public void test0011_combined_binary_expression() {
557
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
558
	this.runConformTest(
559
		new String[] {
560
			"X.java",
561
			"public class X {\n" + 
562
			"public static void main(String args[]) {\n" + 
563
			"    final int max = 30; \n" + 
564
			"    String s[] = new String[max];\n" + 
565
			"    for (int i = 0; i < max; i++) {\n" + 
566
			"        s[i] = \"a\";\n" + 
567
			"    }\n" + 
568
			"    foo(s);\n" + 
569
			"}\n" + 
570
			"static void foo (String s[]) {\n" +
571
			"    final String c = \"a\";" + 
572
			"    System.out.println(\n" + 
573
			"        c + c + c + c + c + c + c + c + c + c + \n" + 
574
			"        c + c + c + c + c + c + c + c + c + c + \n" + 
575
			"        c + c + s[22] + s[23] + s[24] + s[25] + s[26] + \n" +
576
			"        s[27] + s[28] + s[29]\n" + 
577
			"        );\n" + 
578
			"}\n" + 
579
			"}"
580
		},
581
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
582
}
583
584
// AST implementation - visiting binary expressions
585
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
586
// Adding combined binary expressions - checking recursive print
587
public void test0012_combined_binary_expression() {
588
	CombinedBinaryExpression.defaultArityMaxStartingValue = 2; 
589
	runConformTest(
590
		"X.java", 
591
		"public class X {\n" + 
592
		"  void foo() {\n" + 
593
		"    String s1 = \"s1\";\n" + 
594
		"    String s2 = \"s2\";\n" + 
595
		"    String s3 = \"s3\";\n" + 
596
		"    String s4 = \"s4\";\n" + 
597
		"    System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" +
598
		"      s3 + s1 + s4);\n" + 
599
		"  }\n" + 
600
		"}\n",
601
		defaultParser,
602
		new ASTCollector() {
603
			public boolean visit(BinaryExpression binaryExpression, 
604
					BlockScope scope) {
605
				super.visit(binaryExpression, scope);
606
				this.collector.append(binaryExpression);
607
				return true;
608
			}
609
		},
610
		"((((((s1 + \"l1\") + s2) + \"l2\") + s3) + s1) + s4)(((((s1 + \"l1\")" +
611
		" + s2) + \"l2\") + s3) + s1)((((s1 + \"l1\") + s2) + \"l2\") + s3)" +
612
		"(((s1 + \"l1\") + s2) + \"l2\")((s1 + \"l1\") + s2)(s1 + \"l1\")");
613
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
614
		CombinedBinaryExpression.ARITY_MAX_MIN;
615
}
616
617
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
618
// check if the generated code is OK when leveraging CombinedBinaryExpression
619
// variant involving a left-deep right expression at the topmost level
620
public void test0013_combined_binary_expression() {
621
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
622
	this.runConformTest(
623
		new String[] {
624
			"X.java",
625
			"public class X {\n" + 
626
			"public static void main(String args[]) {\n" + 
627
			"    final int max = 30; \n" + 
628
			"    String s[] = new String[max];\n" + 
629
			"    for (int i = 0; i < max; i++) {\n" + 
630
			"        s[i] = \"a\";\n" + 
631
			"    }\n" + 
632
			"    foo(s);\n" + 
633
			"}\n" + 
634
			"static void foo (String s[]) {\n" + 
635
			"    System.out.println(\n" + 
636
			"        \"b\" + (s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + \n" +
637
			"        s[7] + s[8] + s[9] + s[10] + s[11] + s[12] + s[13] +\n" +
638
			"        s[14] + s[15] + s[16] + s[17] + s[18] + s[19] + \n" + 
639
			"        s[20] + s[21] + s[22] + s[23] + s[24] + s[25] + \n" +
640
			"        s[26] + s[27] + s[28] + s[29])\n" + 
641
			"        );\n" + 
642
			"}\n" + 
643
			"}"
644
		},
645
		"baaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
646
}
647
648
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
649
// check if the generated code is OK when leveraging CombinedBinaryExpression
650
// variant involving a left-deep right expression at the topmost level, with
651
// a constant high in tree
652
public void test0014_combined_binary_expression() {
653
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
654
	this.runConformTest(
655
		new String[] {
656
			"X.java",
657
			"public class X {\n" + 
658
			"public static void main(String args[]) {\n" + 
659
			"    final int max = 30; \n" + 
660
			"    String s[] = new String[max];\n" + 
661
			"    for (int i = 0; i < max; i++) {\n" + 
662
			"        s[i] = \"a\";\n" + 
663
			"    }\n" + 
664
			"    foo(s);\n" + 
665
			"}\n" + 
666
			"static void foo (String s[]) {\n" +
667
			"    final String c = \"c\";\n" + 
668
			"    System.out.println(\n" + 
669
			"        \"b\" + \n" +
670
			"         (c + c + c + c + c + c + c + c + c + c + \n" +
671
			"          c + c + c + c + c + c + c + c + c + c + \n" +
672
			"          c + c + s[0])\n" + 
673
			"        );\n" + 
674
			"}\n" + 
675
			"}"
676
		},
677
		"bcccccccccccccccccccccca");
678
}
679
680
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
681
// check if the generated code is OK when leveraging CombinedBinaryExpression
682
// variant involving a left-deep right expression at the topmost level, with
683
// a constant low in tree
684
public void test0015_combined_binary_expression() {
685
	assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN);
686
	this.runConformTest(
687
		new String[] {
688
			"X.java",
689
			"public class X {\n" + 
690
			"public static void main(String args[]) {\n" + 
691
			"    final int max = 30; \n" + 
692
			"    String s[] = new String[max];\n" + 
693
			"    for (int i = 0; i < max; i++) {\n" + 
694
			"        s[i] = \"a\";\n" + 
695
			"    }\n" + 
696
			"    foo(s);\n" + 
697
			"}\n" + 
698
			"static void foo (String s[]) {\n" +
699
			"    final String c = \"c\";\n" + 
700
			"    System.out.println(\n" + 
701
			"        \"b\" + \n" +
702
			"         (c + c + c + c + c + c + c + c + c + c + \n" +
703
			"          c + c + c + c + c + c + c + c + c + c + \n" +
704
			"          s[0] + s[1] + s[2])\n" + 
705
			"        );\n" + 
706
			"}\n" + 
707
			"}"
708
		},
709
		"bccccccccccccccccccccaaa");
710
}
711
712
//AST implementation - binary expressions
713
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728
714
//Adding combined binary expressions - alternate operands
715
public void test0016_combined_binary_expression() {
716
	CombinedBinaryExpression.defaultArityMaxStartingValue = 2; 
717
	this.runConformTest(
718
		"X.java", 
719
		"public class X {\n" + 
720
		"void foo(int i1, int i2, int i3, int i4) {\n" + 
721
		"  System.out.println(i1 - i2 + 0 + i3 + 0 + i4);\n" + 
722
		"}\n" + 
723
		"}\n",
724
		defaultParser,
725
		new ASTCollector() {
726
			public boolean visit(BinaryExpression binaryExpression, 
727
					BlockScope scope) {
728
				super.visit(binaryExpression, scope);
729
				this.collector.append(binaryExpression);
730
				return true;
731
			}
732
		},
733
		"(((((i1 - i2) + 0) + i3) + 0) + i4)((((i1 - i2) + 0) + i3) + 0)" +
734
			"(((i1 - i2) + 0) + i3)((i1 - i2) + 0)(i1 - i2)");
735
	CombinedBinaryExpression.defaultArityMaxStartingValue = 
736
		CombinedBinaryExpression.ARITY_MAX_MIN;
737
}
738
}
739
740
// Helper classes: define visitors leveraged by some tests
741
class ASTCollector extends ASTVisitor {
742
	StringBuffer collector = new StringBuffer();
743
public String result() {
744
	return this.collector.toString();
745
}
746
}
747
748
class ASTBinaryExpressionCollector extends ASTCollector {
749
static final int LIMIT = 30;
750
// help limit the output in length by suppressing the middle
751
// part of strings which length exceeds LIMIT
752
String cut(String source) {
753
	int length;
754
	if ((length = source.length()) > LIMIT) {
755
		StringBuffer result = new StringBuffer(length);
756
		result.append(source.substring(0, LIMIT - 10));
757
		result.append("...");
758
		result.append(source.substring(length - 7, length));
759
		return result.toString();
760
	} else {
761
		return source;
762
	}
763
}
764
public void endVisit(BinaryExpression binaryExpression, BlockScope scope) {
765
	this.collector.append("[ev BE " + cut(binaryExpression.toString()) + "]\n");
766
	super.endVisit(binaryExpression, scope);
767
}
768
769
public void endVisit(CharLiteral charLiteral, BlockScope scope) {
770
	this.collector.append("[ev CL " + cut(charLiteral.toString()) + "]\n");
771
	super.endVisit(charLiteral, scope);
772
}
773
774
public void endVisit(ExtendedStringLiteral literal, BlockScope scope) {
775
	this.collector.append("[ev ESL " + cut(literal.toString()) + "]\n");
776
	super.endVisit(literal, scope);
777
}
778
779
public void endVisit(SingleNameReference singleNameReference, 
780
		BlockScope scope) {
781
	this.collector.append("[ev SNR " + cut(singleNameReference.toString()) + 
782
		"]\n");
783
	super.endVisit(singleNameReference, scope);
784
}
785
786
public void endVisit(StringLiteral stringLiteral, BlockScope scope) {
787
	this.collector.append("[ev SL " + cut(stringLiteral.toString()) + "]\n");
788
	super.endVisit(stringLiteral, scope);
789
}
790
791
public void endVisit(StringLiteralConcatenation literal, BlockScope scope) {
792
	this.collector.append("[ev SLC " + cut(literal.toString()) + "]\n");
793
	super.endVisit(literal, scope);
794
}
795
796
public boolean visit(BinaryExpression binaryExpression, BlockScope scope) {
797
	this.collector.append("[v BE " + cut(binaryExpression.toString()) + "]\n");
798
	return super.visit(binaryExpression, scope);
799
}
800
801
public boolean visit(CharLiteral charLiteral, BlockScope scope) {
802
	this.collector.append("[v CL " + cut(charLiteral.toString()) + "]\n");
803
	return super.visit(charLiteral, scope);
804
}
805
806
public boolean visit(ExtendedStringLiteral literal, BlockScope scope) {
807
	this.collector.append("[v ESL " + cut(literal.toString()) + "]\n");
808
	return super.visit(literal, scope);
809
}
810
811
public boolean visit(SingleNameReference singleNameReference, 
812
		BlockScope scope) {
813
	this.collector.append("[v SNR " + cut(singleNameReference.toString()) + 
814
		"]\n");
815
	return super.visit(singleNameReference, scope);
816
}
817
818
public boolean visit(StringLiteral stringLiteral, BlockScope scope) {
819
	this.collector.append("[v SL " + cut(stringLiteral.toString()) + "]\n");
820
	return super.visit(stringLiteral, scope);
821
}
822
823
public boolean visit(StringLiteralConcatenation literal, BlockScope scope) {
824
	this.collector.append("[v SLC " + cut(literal.toString()) + "]\n");
825
	return super.visit(literal, scope);
826
}
827
}

Return to bug 102728