### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java,v retrieving revision 1.59 diff -u -r1.59 BinaryExpression.java --- compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 28 Mar 2006 20:29:56 -0000 1.59 +++ compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java 26 Jun 2006 10:14:11 -0000 @@ -18,6 +18,19 @@ import org.eclipse.jdt.internal.compiler.lookup.*; public class BinaryExpression extends OperatorExpression { + +/* Tracking helpers + * The following are used to elaborate realistic statistics about binary + * expressions. This must be neutralized in the released code. + * Search the keyword BE_INSTRUMENTATION to reenable. + * An external device must install a suitable probe so as to monitor the + * emission of events and publish the results. + public interface Probe { + public void ping(int depth); + } + public int depthTracker; + public static Probe probe; + */ public Expression left, right; public Constant optimizedBooleanConstant; @@ -28,9 +41,18 @@ this.bits |= operator << ASTNode.OperatorSHIFT; // encode operator this.sourceStart = left.sourceStart; this.sourceEnd = right.sourceEnd; + // BE_INSTRUMENTATION: neutralized in the released code +// if (left instanceof BinaryExpression && +// ((left.bits & OperatorMASK) ^ (this.bits & OperatorMASK)) == 0) { +// this.depthTracker = ((BinaryExpression)left).depthTracker + 1; +// } else { +// this.depthTracker = 1; +// } } -public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { +public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, + FlowInfo flowInfo) { + // keep implementation in sync with CombinedBinaryExpression#analyseCode if (this.resolvedType.id == TypeIds.T_JavaLangString) { return this.right.analyseCode( currentScope, flowContext, @@ -78,6 +100,9 @@ /** * Code generation for a binary operation */ +// given the current focus of CombinedBinaryExpression on strings concatenation, +// we do not provide a general, non-recursive implementation of generateCode, +// but rely upon generateOptimizedStringConcatenationCreation instead public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { int pc = codeStream.position; if (this.constant != Constant.NotAConstant) { @@ -90,6 +115,10 @@ case PLUS : switch (this.bits & ASTNode.ReturnTypeIDMASK) { case T_JavaLangString : + // BE_INSTRUMENTATION: neutralized in the released code +// if (probe != null) { +// probe.ping(this.depthTracker); +// } codeStream.generateStringConcatenationAppend(currentScope, this.left, this.right); if (!valueRequired) codeStream.pop(); @@ -1511,7 +1540,8 @@ } public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) { - + // keep implementation in sync with CombinedBinaryExpression + // #generateOptimizedStringConcatenation /* In the case trying to make a string concatenation, there is no need to create a new * string buffer, thus use a lower-level API for code generation involving only the * appending of arguments to the existing StringBuffer @@ -1542,12 +1572,12 @@ } public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, CodeStream codeStream, int typeID) { - + // keep implementation in sync with CombinedBinaryExpression + // #generateOptimizedStringConcatenationCreation /* In the case trying to make a string concatenation, there is no need to create a new * string buffer, thus use a lower-level API for code generation involving only the * appending of arguments to the existing StringBuffer */ - if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) && ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) { if (this.constant != Constant.NotAConstant) { @@ -1579,6 +1609,123 @@ return true; } +/** + * Separates into a reusable method the subpart of {@link + * #resolveType(BlockScope)} that needs to be executed while climbing up the + * chain of expressions of this' leftmost branch. For use by {@link + * CombinedBinaryExpression#resolveType(BlockScope)}. + * @param scope the scope within which the resolution occurs + */ +void nonRecursiveResolveTypeUpwards(BlockScope scope) { + // keep implementation in sync with BinaryExpression#resolveType + boolean leftIsCast, rightIsCast; + TypeBinding leftType = this.left.resolvedType; + + if ((rightIsCast = this.right instanceof CastExpression) == true) { + this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on + } + TypeBinding rightType = this.right.resolveType(scope); + + // use the id of the type to navigate into the table + if (leftType == null || rightType == null) { + this.constant = Constant.NotAConstant; + return; + } + + int leftTypeID = leftType.id; + int rightTypeID = rightType.id; + + // autoboxing support + boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; + if (use15specifics) { + if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString && rightTypeID != TypeIds.T_null) { + leftTypeID = scope.environment().computeBoxingType(leftType).id; + } + if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) { + rightTypeID = scope.environment().computeBoxingType(rightType).id; + } + } + if (leftTypeID > 15 + || rightTypeID > 15) { // must convert String + Object || Object + String + if (leftTypeID == TypeIds.T_JavaLangString) { + rightTypeID = TypeIds.T_JavaLangObject; + } else if (rightTypeID == TypeIds.T_JavaLangString) { + leftTypeID = TypeIds.T_JavaLangObject; + } else { + this.constant = Constant.NotAConstant; + scope.problemReporter().invalidOperator(this, leftType, rightType); + return; + } + } + if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) { + if (leftTypeID == TypeIds.T_JavaLangString) { + this.left.computeConversion(scope, leftType, leftType); + if (rightType.isArrayType() && ((ArrayBinding) rightType).elementsType() == TypeBinding.CHAR) { + scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right); + } + } + if (rightTypeID == TypeIds.T_JavaLangString) { + this.right.computeConversion(scope, rightType, rightType); + if (leftType.isArrayType() && ((ArrayBinding) leftType).elementsType() == TypeBinding.CHAR) { + scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left); + } + } + } + + // the code is an int + // (cast) left Op (cast) right --> result + // 0000 0000 0000 0000 0000 + // <<16 <<12 <<8 <<4 <<0 + + // Don't test for result = 0. If it is zero, some more work is done. + // On the one hand when it is not zero (correct code) we avoid doing the test + int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT; + int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID]; + + this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType); + this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType); + this.bits |= operatorSignature & 0xF; + switch (operatorSignature & 0xF) { // record the current ReturnTypeID + // only switch on possible result type..... + case T_boolean : + this.resolvedType = TypeBinding.BOOLEAN; + break; + case T_byte : + this.resolvedType = TypeBinding.BYTE; + break; + case T_char : + this.resolvedType = TypeBinding.CHAR; + break; + case T_double : + this.resolvedType = TypeBinding.DOUBLE; + break; + case T_float : + this.resolvedType = TypeBinding.FLOAT; + break; + case T_int : + this.resolvedType = TypeBinding.INT; + break; + case T_long : + this.resolvedType = TypeBinding.LONG; + break; + case T_JavaLangString : + this.resolvedType = scope.getJavaLangString(); + break; + default : //error........ + this.constant = Constant.NotAConstant; + scope.problemReporter().invalidOperator(this, leftType, rightType); + return; + } + + // check need for operand cast + if ((leftIsCast = (this.left instanceof CastExpression)) == true || + rightIsCast) { + CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID, leftIsCast, this.right, rightTypeID, rightIsCast); + } + // compute the constant when valid + computeConstant(scope, leftTypeID, rightTypeID); +} + public void optimizedBooleanConstant(int leftId, int operator, int rightId) { switch (operator) { case AND : @@ -1628,11 +1775,15 @@ } public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) { + // keep implementation in sync with + // CombinedBinaryExpression#printExpressionNoParenthesis this.left.printExpression(indent, output).append(' ').append(operatorToString()).append(' '); return this.right.printExpression(0, output); } - + public TypeBinding resolveType(BlockScope scope) { + // keep implementation in sync with CombinedBinaryExpression#resolveType + // and nonRecursiveResolveTypeUpwards boolean leftIsCast, rightIsCast; if ((leftIsCast = this.left instanceof CastExpression) == true) this.left.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on TypeBinding leftType = this.left.resolveType(scope); Index: compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java,v retrieving revision 1.347 diff -u -r1.347 Parser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 21 Jun 2006 14:39:35 -0000 1.347 +++ compiler/org/eclipse/jdt/internal/compiler/parser/Parser.java 26 Jun 2006 10:14:15 -0000 @@ -1528,8 +1528,39 @@ } else { this.expressionStack[this.expressionPtr] = new BinaryExpression(expr1, expr2, PLUS); } + } else if (expr1 instanceof CombinedBinaryExpression) { + CombinedBinaryExpression cursor; + // left branch is comprised of PLUS BEs + // cursor is shifted upwards, while needed BEs are added + // on demand; past the arityMax-th + // consecutive BE, a CBE is inserted that holds a + // full-fledged references table + if ((cursor = (CombinedBinaryExpression)expr1).arity < + cursor.arityMax) { + cursor.left = new BinaryExpression(cursor.left, + cursor.right, PLUS); + cursor.arity++; + } else { + cursor.left = new CombinedBinaryExpression(cursor.left, + cursor.right, PLUS, cursor.arity); + cursor.arity = 0; + cursor.tuneArityMax(); + } + cursor.right = expr2; + cursor.sourceEnd = expr2.sourceEnd; + this.expressionStack[this.expressionPtr] = cursor; + // BE_INSTRUMENTATION: neutralized in the released code +// cursor.depthTracker = ((BinaryExpression)cursor.left). +// depthTracker + 1; + } else if (expr1 instanceof BinaryExpression) { + this.expressionStack[this.expressionPtr] = + new CombinedBinaryExpression(expr1, expr2, PLUS, 1); } else { - this.expressionStack[this.expressionPtr] = new BinaryExpression(expr1, expr2, PLUS); + // single out the a + b case, which is a BE + // instead of a CBE (slightly more than a half of strings + // concatenation are one-deep binary expressions) + this.expressionStack[this.expressionPtr] = + new BinaryExpression(expr1, expr2, PLUS); } } else if (expr1 instanceof StringLiteral) { if (expr2 instanceof StringLiteral) { @@ -1537,19 +1568,38 @@ this.expressionStack[this.expressionPtr] = ((StringLiteral) expr1).extendsWith((StringLiteral) expr2); } else { + // single out the a + b case this.expressionStack[this.expressionPtr] = - new BinaryExpression( - expr1, - expr2, - op); + new BinaryExpression(expr1, expr2, PLUS); + } + } else if (expr1 instanceof CombinedBinaryExpression) { + CombinedBinaryExpression cursor; + // shift cursor; create BE/CBE as needed + if ((cursor = (CombinedBinaryExpression)expr1).arity < + cursor.arityMax) { + cursor.left = new BinaryExpression(cursor.left, + cursor.right, PLUS); + cursor.arity++; + } else { + cursor.left = new CombinedBinaryExpression(cursor.left, + cursor.right, PLUS, cursor.arity); + cursor.arity = 0; + cursor.tuneArityMax(); + } + cursor.right = expr2; + cursor.sourceEnd = expr2.sourceEnd; + // BE_INSTRUMENTATION: neutralized in the released code +// cursor.depthTracker = ((BinaryExpression)cursor.left). +// depthTracker + 1; + this.expressionStack[this.expressionPtr] = cursor; + } else if (expr1 instanceof BinaryExpression) { + this.expressionStack[this.expressionPtr] = + new CombinedBinaryExpression(expr1, expr2, PLUS, 1); + } else { + // single out the a + b case + this.expressionStack[this.expressionPtr] = + new BinaryExpression(expr1, expr2, PLUS); } - } else { - this.expressionStack[this.expressionPtr] = - new BinaryExpression( - expr1, - expr2, - op); - } break; case LESS : this.intPtr--; @@ -1619,6 +1669,8 @@ */ Expression expr1 = this.expressionStack[this.expressionPtr + 1]; Expression expr2 = this.expressionStack[this.expressionPtr]; + // Note: we do not attempt to promote BinaryExpression-s to + // IndexedBinaryExpression-s here since expr1 always holds a name switch(op) { case OR_OR : this.expressionStack[this.expressionPtr] = Index: compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java diff -N compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,393 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.codegen.CodeStream; +import org.eclipse.jdt.internal.compiler.flow.FlowContext; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; + +/** + * CombinedBinaryExpression is an implementation of BinaryExpression that + * specifically attempts to mitigate the issues raised by expressions which + * have a very deep leftmost branch. It does so by maintaining a table of + * direct references to its subexpressions, and implementing non-recursive + * variants of the most significant recursive algorithms of its ancestors. + * The subexpressions table only holds intermediate binary expressions. Its + * role is to provide the reversed navigation through the left relationship + * of BinaryExpression to Expression. To cope with potentially very deep + * left branches, an instance of CombinedBinaryExpression is created once in + * a while, using variable thresholds held by {@link #arityMax}. + * As a specific case, the topmost node of all binary expressions that are + * deeper than one is a CombinedBinaryExpression, but it has no references + * table.
+ * Notes: + * + */ +public class CombinedBinaryExpression extends BinaryExpression { + + /** + * The number of consecutive binary expressions of this' left branch that + * bear the same operator as this.
+ * Notes: + * + */ + public int arity; + + /** + * The threshold that will trigger the creation of the next full-fledged + * CombinedBinaryExpression. This field is only maintained for the + * topmost binary expression (it is 0 otherwise). It enables a variable + * policy, which scales better with very large expressions. + */ + public int arityMax; + + /** + * Upper limit for {@link #arityMax}. + */ + public static final int ARITY_MAX_MAX = 160; + + /** + * Default lower limit for {@link #arityMax}. + */ + public static final int ARITY_MAX_MIN = 20; + + /** + * Default value for the first term of the series of {@link #arityMax} + * values. Changing this allows for experimentation. Not meant to be + * changed during a parse operation. + */ + public static int defaultArityMaxStartingValue = ARITY_MAX_MIN; + + /** + * A table of references to the binary expressions of this' left branch. + * Instances of CombinedBinaryExpression are not repeated here. Instead, + * the left subexpression of referencesTable[0] may be a combined binary + * expression, if appropriate. Null when this only cares about tracking + * the expression's arity. + */ + public BinaryExpression referencesTable[]; + +/** + * Make a new CombinedBinaryExpression. If arity is strictly greater than one, + * a references table is built and initialized with the reverse relationship of + * the one defined by {@link BinaryExpression#left}. arity and left must be + * compatible with each other (that is, there must be at least arity - 1 + * consecutive compatible binary expressions into the leftmost branch of left, + * the topmost of which being left's immediate left expression). + * @param left the left branch expression + * @param right the right branch expression + * @param operator the operator for this binary expression - only PLUS for now + * @param arity the number of binary expressions of a compatible operator that + * already exist into the leftmost branch of left (including left); must + * be strictly greater than 0 + */ +public CombinedBinaryExpression(Expression left, Expression right, int operator, + int arity) { + super(left, right, operator); + this.arity = arity; + if (arity > 1) { + this.referencesTable = new BinaryExpression[arity]; + this.referencesTable[arity - 1] = (BinaryExpression) left; + for (int i = arity - 1; i > 0; i--) { + this.referencesTable[i - 1] = + (BinaryExpression) this.referencesTable[i].left; + } + } else { + this.arityMax = defaultArityMaxStartingValue; + } +} + +public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, + FlowInfo flowInfo) { + // keep implementation in sync with BinaryExpression#analyseCode + if (this.referencesTable == null) { + return super.analyseCode(currentScope, flowContext, flowInfo); + } + BinaryExpression cursor; + if ((cursor = this.referencesTable[0]).resolvedType.id != + TypeIds.T_JavaLangString) { + cursor.left.checkNPE(currentScope, flowContext, flowInfo); + } + flowInfo = cursor.left.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + for (int i = 0, end = this.arity; i < end; i ++) { + if ((cursor = this.referencesTable[i]).resolvedType.id != + TypeIds.T_JavaLangString) { + cursor.right.checkNPE(currentScope, flowContext, flowInfo); + } + flowInfo = cursor.right. + analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + } + if (this.resolvedType.id != TypeIds.T_JavaLangString) { + this.right.checkNPE(currentScope, flowContext, flowInfo); + } + return this.right.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); +} + +public void generateOptimizedStringConcatenation(BlockScope blockScope, + CodeStream codeStream, int typeID) { + // keep implementation in sync with BinaryExpression and Expression + // #generateOptimizedStringConcatenation + if (this.referencesTable == null) { + super.generateOptimizedStringConcatenation(blockScope, codeStream, + typeID); + } else { + if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == + OperatorIds.PLUS) + && ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) { + if (this.constant != Constant.NotAConstant) { + codeStream.generateConstant(this.constant, this.implicitConversion); + codeStream.invokeStringConcatenationAppendForType( + this.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + } else { + BinaryExpression cursor = this.referencesTable[0]; + + int restart = 0; + // int cursorTypeID; + int pc = codeStream.position; + for (restart = this.arity - 1; restart >= 0; restart--) { + if ((cursor = this.referencesTable[restart]).constant != + Constant.NotAConstant) { + codeStream.generateConstant(cursor.constant, + cursor.implicitConversion); + codeStream.invokeStringConcatenationAppendForType( + cursor.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + break; + } + // never happens for now - may reconsider if we decide to + // cover more than string concatenation + // if (!((((cursor = this.referencesTable[restart]).bits & + // ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == + // OperatorIds.PLUS) & + // ((cursorTypeID = cursor.bits & ASTNode.ReturnTypeIDMASK) == + // TypeIds.T_JavaLangString)) { + // if (cursorTypeID == T_JavaLangString && + // cursor.constant != Constant.NotAConstant && + // cursor.constant.stringValue().length() == 0) { + // break; // optimize str + "" + // } + // cursor.generateCode(blockScope, codeStream, true); + // codeStream.invokeStringConcatenationAppendForType( + // cursorTypeID); + // break; + // } + } + restart++; + if (restart == 0) { // reached the leftmost expression + cursor.left.generateOptimizedStringConcatenation( + blockScope, + codeStream, + cursor.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + } + int pcAux; + for (int i = restart; i < this.arity; i++) { + codeStream.recordPositionsFrom(pc, + (cursor = this.referencesTable[i]).left.sourceStart); + pcAux = codeStream.position; + cursor.right.generateOptimizedStringConcatenation(blockScope, + codeStream, cursor.right.implicitConversion & + TypeIds.COMPILE_TYPE_MASK); + codeStream.recordPositionsFrom(pcAux, cursor.right.sourceStart); + } + codeStream.recordPositionsFrom(pc, this.left.sourceStart); + pc = codeStream.position; + this.right.generateOptimizedStringConcatenation( + blockScope, + codeStream, + this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + codeStream.recordPositionsFrom(pc, this.right.sourceStart); + } + } else { + super.generateOptimizedStringConcatenation(blockScope, codeStream, + typeID); + } + } +} + +public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, + CodeStream codeStream, int typeID) { + // keep implementation in sync with BinaryExpression + // #generateOptimizedStringConcatenationCreation + if (this.referencesTable == null) { + super.generateOptimizedStringConcatenationCreation(blockScope, + codeStream, typeID); + } else { + if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == + OperatorIds.PLUS) && + ((this.bits & ASTNode.ReturnTypeIDMASK) == + TypeIds.T_JavaLangString) && + this.constant == Constant.NotAConstant) { + int pc = codeStream.position; + BinaryExpression cursor = this.referencesTable[this.arity - 1]; + // silence warnings + int restart = 0; + for (restart = this.arity - 1; restart >= 0; restart--) { + if (((((cursor = this.referencesTable[restart]).bits & + ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == + OperatorIds.PLUS) && + ((cursor.bits & ASTNode.ReturnTypeIDMASK) == + TypeIds.T_JavaLangString)) { + if (cursor.constant != Constant.NotAConstant) { + codeStream.newStringContatenation(); // new: java.lang.StringBuffer + codeStream.dup(); + codeStream.ldc(cursor.constant.stringValue()); + codeStream.invokeStringConcatenationStringConstructor(); + // invokespecial: java.lang.StringBuffer.(Ljava.lang.String;)V + break; + } + } else { + cursor.generateOptimizedStringConcatenationCreation(blockScope, + codeStream, cursor.implicitConversion & + TypeIds.COMPILE_TYPE_MASK); + break; + } + } + restart++; + if (restart == 0) { // reached the leftmost expression + cursor.left.generateOptimizedStringConcatenationCreation( + blockScope, + codeStream, + this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + } + int pcAux; + for (int i = restart; i < this.arity; i++) { + codeStream.recordPositionsFrom(pc, + (cursor = this.referencesTable[i]).left.sourceStart); + pcAux = codeStream.position; + cursor.right.generateOptimizedStringConcatenation(blockScope, + codeStream, cursor.right.implicitConversion & + TypeIds.COMPILE_TYPE_MASK); + codeStream.recordPositionsFrom(pcAux, cursor.right.sourceStart); + } + codeStream.recordPositionsFrom(pc, this.left.sourceStart); + pc = codeStream.position; + this.right.generateOptimizedStringConcatenation( + blockScope, + codeStream, + this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK); + codeStream.recordPositionsFrom(pc, this.right.sourceStart); + } else { + super.generateOptimizedStringConcatenationCreation(blockScope, + codeStream, typeID); + } + } +} + +public StringBuffer printExpressionNoParenthesis(int indent, + StringBuffer output) { + // keep implementation in sync with + // BinaryExpression#printExpressionNoParenthesis and + // OperatorExpression#printExpression + if (this.referencesTable == null) { + return super.printExpressionNoParenthesis(indent, output); + } + String operatorString = operatorToString(); + for (int i = this.arity - 1; i >= 0; i--) { + output.append('('); + } + output = this.referencesTable[0].left. + printExpression(indent, output); + for (int i = 0, end = this.arity; + i < end; i++) { + output.append(' ').append(operatorString).append(' '); + output = this.referencesTable[i].right. + printExpression(0, output); + output.append(')'); + } + output.append(' ').append(operatorString).append(' '); + return this.right.printExpression(0, output); +} + +public TypeBinding resolveType(BlockScope scope) { + // keep implementation in sync with BinaryExpression#resolveType + if (this.referencesTable == null) { + return super.resolveType(scope); + } + BinaryExpression cursor; + if ((cursor = this.referencesTable[0]).left instanceof CastExpression) { + cursor.left.bits |= ASTNode.DisableUnnecessaryCastCheck; + // will check later on + } + cursor.left.resolveType(scope); + for (int i = 0, end = this.arity; i < end; i ++) { + this.referencesTable[i].nonRecursiveResolveTypeUpwards(scope); + } + nonRecursiveResolveTypeUpwards(scope); + return this.resolvedType; +} + +public void traverse(ASTVisitor visitor, BlockScope scope) { + if (this.referencesTable == null) { + super.traverse(visitor, scope); + } else { + if (visitor.visit(this, scope)) { + int restart; + for (restart = this.arity - 1; + restart >= 0; + restart--) { + if (!visitor.visit( + this.referencesTable[restart], scope)) { + visitor.endVisit( + this.referencesTable[restart], scope); + break; + } + } + restart++; + // restart now points to the deepest BE for which + // visit returned true, if any + if (restart == 0) { + this.referencesTable[0].left.traverse(visitor, scope); + } + for (int i = restart, end = this.arity; + i < end; i++) { + this.referencesTable[i].right.traverse(visitor, scope); + visitor.endVisit(this.referencesTable[i], scope); + } + this.right.traverse(visitor, scope); + } + visitor.endVisit(this, scope); + } +} + +/** + * Change {@link #arityMax} if and as needed. The current policy is to double + * arityMax each time this method is called, until it reaches + * {@link #ARITY_MAX_MAX}. Other policies may consider incrementing it less + * agressively. Call only after an appropriate value has been assigned to + * {@link #left}. + */ +// more sophisticate increment policies would leverage the leftmost expression +// to hold an indication of the number of uses of a given arityMax in a row +public void tuneArityMax() { + if (this.arityMax < ARITY_MAX_MAX) { + this.arityMax *= 2; + } +} +} #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java,v retrieving revision 1.13 diff -u -r1.13 FlowAnalysisTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java 19 Apr 2006 12:26:46 -0000 1.13 +++ src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java 26 Jun 2006 10:14:23 -0000 @@ -752,6 +752,31 @@ "The local variable s may not have been initialized\n" + "----------\n"); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Non-recursive approach for deep binary expressions. Check that the +// flow analysis doesn't break. +public void test027() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public static void main(String args[]) {\n" + + " String s;\n" + + " if (args.length == 0) {\n" + + " s = \"s\";\n" + + " } else {\n" + + " s = args[0];\n" + + " }\n" + + " System.out.println(s + \"-\" + s + \"-\" + s + \"-\" +\n" + + " s + \"-\" + s + \"-\" + s + \"-\" +\n" + + " s + \"-\" + s + \"-\" + s + \"-\" +\n" + + " s + \"-\" + s + \"-\" + s + \"-\" +\n" + + " s + \"-\" + s + \"-\" + s + \"-\");\n" + + " }\n" + + "}" + }, + "s-s-s-s-s-s-s-s-s-s-s-s-s-s-s-"); +} public static Class testClass() { return FlowAnalysisTest.class; } Index: src/org/eclipse/jdt/core/tests/compiler/regression/XLargeTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/XLargeTest.java,v retrieving revision 1.9 diff -u -r1.9 XLargeTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/XLargeTest.java 29 Mar 2006 03:50:22 -0000 1.9 +++ src/org/eclipse/jdt/core/tests/compiler/regression/XLargeTest.java 26 Jun 2006 10:14:27 -0000 @@ -11,6 +11,7 @@ package org.eclipse.jdt.core.tests.compiler.regression; import java.util.Map; +import java.util.Random; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; @@ -3581,6 +3582,98 @@ }, "true"); } + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Failed before using a non recursive implementation of deep binary +// expressions. +public void test010() { + StringBuffer sourceCode = new StringBuffer( + "public class X {\n" + + " void foo(String a, String b, String c, String d, String e) {\n" + + " String s = \n"); + for (int i = 0; i < 350; i++) { + sourceCode.append( + " \"abcdef\" + a + b + c + d + e + " + + "\" ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno" + + "pqrstuvwxyzabcdefghijklmnopqrstuvwxyz\" +\n"); + } + sourceCode.append( + " \"abcdef\" + a + b + c + d + e + \" ghijklmnopqrstuvwxyz" + + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + + "abcdefghijklmnopqrstuvwxy12\";\n" + + " }\n" + + "}"); + this.runConformTest( + new String[] { + "X.java", + sourceCode.toString() + }, + ""); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if we hit the 64Kb limit on method code lenth in class files before +// filling the stack +// need to use a computed string (else this source file will get blown away +// as well) +public void test011() { + int length = 3 * 54 * 1000; + // the longer the slower, but still needs to reach the limit... + StringBuffer veryLongString = new StringBuffer(length + 20); + veryLongString.append('"'); + Random random = new Random(); + while (veryLongString.length() < length) { + veryLongString.append("\"+a+\""); + veryLongString.append(random.nextLong()); + } + veryLongString.append('"'); + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(String a, String b, String c, String d, String e) {\n" + + " String s = \n" + + veryLongString.toString() + + " + \"abcdef\" + a + b + c + d + e + \" ghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxyzabcdefghiABCDEFGHIJKLMNOPQRSTUVWXYZjklmnopqrstuvwxy12\";\n" + + " }\n" + + "}" + }, + "----------\n" + + "1. ERROR in X.java (at line 2)\n" + + " void foo(String a, String b, String c, String d, String e) {\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "The code of method foo(String, String, String, String, String) is " + + "exceeding the 65535 bytes limit\n" + + "----------\n"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// variant: right member of the topmost expression is left-deep +public void test012() { + StringBuffer sourceCode = new StringBuffer( + "public class X {\n" + + " void foo(String a, String b, String c, String d, String e) {\n" + + " String s = a + (\n"); + for (int i = 0; i < 1000; i++) { + sourceCode.append( + " \"abcdef\" + a + b + c + d + e + " + + "\" ghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno" + + "pqrstuvwxyzabcdefghijklmnopqrstuvwxyz\" +\n"); + } + sourceCode.append( + " \"abcdef\" + a + b + c + d + e + \" ghijklmnopqrstuvwxyz" + + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" + + "abcdefghijklmnopqrstuvwxy12\");\n" + + " }\n" + + "}"); + this.runConformTest( + new String[] { + "X.java", + sourceCode.toString() + }, + ""); +} + public static Class testClass() { return XLargeTest.class; } Index: src/org/eclipse/jdt/core/tests/compiler/regression/ASTImplTests.java =================================================================== RCS file: src/org/eclipse/jdt/core/tests/compiler/regression/ASTImplTests.java diff -N src/org/eclipse/jdt/core/tests/compiler/regression/ASTImplTests.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jdt/core/tests/compiler/regression/ASTImplTests.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,800 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import junit.framework.Test; + +import org.eclipse.jdt.core.tests.util.Util; +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; +import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; +import org.eclipse.jdt.internal.compiler.ast.CharLiteral; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.CombinedBinaryExpression; +import org.eclipse.jdt.internal.compiler.ast.ExtendedStringLiteral; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.StringLiteral; +import org.eclipse.jdt.internal.compiler.ast.StringLiteralConcatenation; +import org.eclipse.jdt.internal.compiler.batch.CompilationUnit; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.parser.Parser; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; +import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; + +/** + * A tests series especially meant to validate the internals of our AST + * implementation. + */ +public class ASTImplTests extends AbstractRegressionTest { +public ASTImplTests(String name) { + super(name); +} + + // Static initializer to specify tests subset using TESTS_* static variables + // All specified tests which does not belong to the class are skipped... + // Only the highest compliance level is run; add the VM argument + // -Dcompliance=1.4 (for example) to lower it if needed + static { +// TESTS_NAMES = new String[] { "test2050" }; +// TESTS_NUMBERS = new int[] { 3 }; +// TESTS_NUMBERS = new int[] { 2999 }; +// TESTS_RANGE = new int[] { 2050, -1 }; + } + +public static Test suite() { + return buildAllCompliancesTestSuite(testClass()); +} + +public static Class testClass() { + return ASTImplTests.class; +} + +// Helper methods +static Parser defaultParser = new Parser( + new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), + new CompilerOptions(), + new DefaultProblemFactory()), false); +public void runConformTest(String fileName, String fileContents, + Parser parser, ASTCollector visitor, String expected) { + CompilationUnit source = + new CompilationUnit(fileContents.toCharArray(), fileName, null); + CompilationResult compilationResult = + new CompilationResult(source, 1, 1, 10); + CompilationUnitDeclaration unit = parser.parse(source, compilationResult); + assertEquals(0, compilationResult.problemCount); + unit.traverse(visitor, unit.scope); + String result = visitor.result(); + if (! expected.equals(result)) { + System.out.println(getClass().getName() + '#' + getName()); + System.out.println("Expected:"); + System.out.println(expected); + System.out.println("But was:"); + System.out.println(result); + System.out.println("Cut and paste:"); + System.out.println(Util.displayString(result, INDENT, SHIFT)); + } + assertEquals(expected, result); +} + +// AST implementation - visiting binary expressions +public void test0001_regular_binary_expression() { + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + \"l3\" + s4);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector(), + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v SL \"s3\"]\n" + + "[ev SL \"s3\"]\n" + + "[v SL \"s4\"]\n" + + "[ev SL \"s4\"]\n" + + "[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + + "[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v BE ((s1 + \"l1\") + s2)]\n" + + "[v BE (s1 + \"l1\")]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[v SL \"l1\"]\n" + + "[ev SL \"l1\"]\n" + + "[ev BE (s1 + \"l1\")]\n" + + "[v SNR s2]\n" + + "[ev SNR s2]\n" + + "[ev BE ((s1 + \"l1\") + s2)]\n" + + "[v SL \"l2\"]\n" + + "[ev SL \"l2\"]\n" + + "[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v SNR s3]\n" + + "[ev SNR s3]\n" + + "[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v SL \"l3\"]\n" + + "[ev SL \"l3\"]\n" + + "[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v SNR s4]\n" + + "[ev SNR s4]\n" + + "[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n"); +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions +public void test0002_combined_binary_expression() { + CombinedBinaryExpression.defaultArityMaxStartingValue = 3; + // one CBE each fourth BE + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + \"l3\" + s4);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector() { + public void endVisit(BinaryExpression binaryExpression, BlockScope scope) { + if (binaryExpression instanceof CombinedBinaryExpression && + ((CombinedBinaryExpression) binaryExpression). + referencesTable != null) { + this.collector.append("[ev CBE " + + cut(binaryExpression.toString()) + "]\n"); + } else { + super.endVisit(binaryExpression, scope); + } + } + }, + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v SL \"s3\"]\n" + + "[ev SL \"s3\"]\n" + + "[v SL \"s4\"]\n" + + "[ev SL \"s4\"]\n" + + "[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + + "[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v BE ((s1 + \"l1\") + s2)]\n" + + "[v BE (s1 + \"l1\")]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[v SL \"l1\"]\n" + + "[ev SL \"l1\"]\n" + + "[ev BE (s1 + \"l1\")]\n" + + "[v SNR s2]\n" + + "[ev SNR s2]\n" + + "[ev BE ((s1 + \"l1\") + s2)]\n" + + "[v SL \"l2\"]\n" + + "[ev SL \"l2\"]\n" + + "[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v SNR s3]\n" + + "[ev SNR s3]\n" + + "[ev CBE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v SL \"l3\"]\n" + + "[ev SL \"l3\"]\n" + + "[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v SNR s4]\n" + + "[ev SNR s4]\n" + + "[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n"); + CombinedBinaryExpression.defaultArityMaxStartingValue = + CombinedBinaryExpression.ARITY_MAX_MIN; +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions +public void test0003_combined_binary_expression() { + Parser parser = new Parser( + new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), + new CompilerOptions(), + new DefaultProblemFactory()), true); // optimize string literals + CombinedBinaryExpression.defaultArityMaxStartingValue = 2; + // one CBE each third BE - except the top one, which is degenerate (no + // references table) + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + \"l3\" + s4);\n" + + " }\n" + + "}\n", + parser, + new ASTBinaryExpressionCollector() { + public void endVisit(BinaryExpression binaryExpression, BlockScope scope) { + if (binaryExpression instanceof CombinedBinaryExpression && + ((CombinedBinaryExpression) binaryExpression). + referencesTable != null) { + this.collector.append("[ev CBE " + + cut(binaryExpression.toString()) + "]\n"); + } else { + super.endVisit(binaryExpression, scope); + } + } + }, + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v SL \"s3\"]\n" + + "[ev SL \"s3\"]\n" + + "[v SL \"s4\"]\n" + + "[ev SL \"s4\"]\n" + + "[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + + "[v BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v BE ((s1 + \"l1\") + s2)]\n" + + "[v BE (s1 + \"l1\")]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[v SL \"l1\"]\n" + + "[ev SL \"l1\"]\n" + + "[ev BE (s1 + \"l1\")]\n" + + "[v SNR s2]\n" + + "[ev SNR s2]\n" + + "[ev BE ((s1 + \"l1\") + s2)]\n" + + "[v SL \"l2\"]\n" + + "[ev SL \"l2\"]\n" + + "[ev CBE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v SNR s3]\n" + + "[ev SNR s3]\n" + + "[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v SL \"l3\"]\n" + + "[ev SL \"l3\"]\n" + + "[ev BE (((((s1 + \"l1\") + s2...+ \"l3\")]\n" + + "[v SNR s4]\n" + + "[ev SNR s4]\n" + + "[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n"); + CombinedBinaryExpression.defaultArityMaxStartingValue = + CombinedBinaryExpression.ARITY_MAX_MIN; +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - effect of a literal at the start with +// string literal optimization +public void test0004_combined_binary_expression() { + Parser parser = new Parser( + new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(), + new CompilerOptions(), + new DefaultProblemFactory()), true); // optimize string literals + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " System.out.println(\"l\" + \"1\" + s1);\n" + + // "l" + "1" is collapsed into "l1" without affecting binary + // expressions: only one BE + " }\n" + + "}\n", + parser, + new ASTBinaryExpressionCollector(), + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v BE (ExtendedStringLiter...} + s1)]\n" + + "[v ESL ExtendedStringLiteral{l1}]\n" + + "[ev ESL ExtendedStringLiteral{l1}]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[ev BE (ExtendedStringLiter...} + s1)]\n"); +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - effect of a literal at the start without +// string literals optimization +public void test0005_combined_binary_expression() { + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " System.out.println(\"l\" + \"1\" + s1);\n" + + // "l" + "1" is handled by a string literal concatenation without + // affecting binary expressions: only one BE + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector(), + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v BE (StringLiteralConcat...} + s1)]\n" + + "[v SLC StringLiteralConcate...\n" + + "\"1\"+\n" + + "}]\n" + + "[v SL \"l\"]\n" + + "[ev SL \"l\"]\n" + + "[v SL \"1\"]\n" + + "[ev SL \"1\"]\n" + + "[ev SLC StringLiteralConcate...\n" + + "\"1\"+\n" + + "}]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[ev BE (StringLiteralConcat...} + s1)]\n"); +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - cutting the traversal half-way down +public void test0006_combined_binary_expression() { + CombinedBinaryExpression.defaultArityMaxStartingValue = 1; + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + s1 + s4);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector() { + public boolean visit(BinaryExpression binaryExpression, BlockScope scope) { + super.visit(binaryExpression, scope); + if (binaryExpression.right instanceof StringLiteral) { + return false; + } + return true; + } + }, + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v SL \"s3\"]\n" + + "[ev SL \"s3\"]\n" + + "[v SL \"s4\"]\n" + + "[ev SL \"s4\"]\n" + + "[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + + "[v BE (((((s1 + \"l1\") + s2...) + s1)]\n" + + "[v BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[ev BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v SNR s3]\n" + + "[ev SNR s3]\n" + + "[ev BE ((((s1 + \"l1\") + s2)...) + s3)]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[ev BE (((((s1 + \"l1\") + s2...) + s1)]\n" + + "[v SNR s4]\n" + + "[ev SNR s4]\n" + + "[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n"); + CombinedBinaryExpression.defaultArityMaxStartingValue = + CombinedBinaryExpression.ARITY_MAX_MIN; +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - cutting the traversal right away +public void test0007_combined_binary_expression() { + CombinedBinaryExpression.defaultArityMaxStartingValue = 4; + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + \"l3\" + s4);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector() { + public boolean visit(BinaryExpression binaryExpression, BlockScope scope) { + super.visit(binaryExpression, scope); + return false; + } + }, + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v SL \"s3\"]\n" + + "[ev SL \"s3\"]\n" + + "[v SL \"s4\"]\n" + + "[ev SL \"s4\"]\n" + + "[v BE ((((((s1 + \"l1\") + s...) + s4)]\n" + + "[ev BE ((((((s1 + \"l1\") + s...) + s4)]\n"); + CombinedBinaryExpression.defaultArityMaxStartingValue = + CombinedBinaryExpression.ARITY_MAX_MIN; +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - case of one-deep expression +public void test0008_combined_binary_expression() { + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\");\n" + + " System.out.println(s1 + s2);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTBinaryExpressionCollector() { + public void endVisit(BinaryExpression binaryExpression, BlockScope scope) { + if (binaryExpression instanceof CombinedBinaryExpression) { + this.collector.append("[ev CBE " + + cut(binaryExpression.toString()) + "]\n"); + } else { + super.endVisit(binaryExpression, scope); + } + } + }, + "[v SL \"s1\"]\n" + + "[ev SL \"s1\"]\n" + + "[v SL \"s2\"]\n" + + "[ev SL \"s2\"]\n" + + "[v BE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v BE ((s1 + \"l1\") + s2)]\n" + + "[v BE (s1 + \"l1\")]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[v SL \"l1\"]\n" + + "[ev SL \"l1\"]\n" + + "[ev BE (s1 + \"l1\")]\n" + + "[v SNR s2]\n" + + "[ev SNR s2]\n" + + "[ev BE ((s1 + \"l1\") + s2)]\n" + + "[v SL \"l2\"]\n" + + "[ev SL \"l2\"]\n" + + "[ev CBE (((s1 + \"l1\") + s2) + \"l2\")]\n" + + "[v BE (s1 + s2)]\n" + + "[v SNR s1]\n" + + "[ev SNR s1]\n" + + "[v SNR s2]\n" + + "[ev SNR s2]\n" + + "[ev BE (s1 + s2)]\n"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +public void test0009_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " System.out.println(\n" + + " s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + \n" + + " s[7] + s[8] + s[9] + s[10] + s[11] + s[12] + s[13] +\n" + + " s[14] + s[15] + s[16] + s[17] + s[18] + s[19] + \n" + + " s[20] + s[21] + s[22] + s[23] + s[24] + s[25] + \n" + + " s[26] + s[27] + s[28] + s[29]\n" + + " );\n" + + "}\n" + + "}"}, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +// variant involving constant binary expressions deep in the tree +public void test0010_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " final String c = \"a\";" + + " System.out.println(\n" + + " c + c + c + c + s[4] + s[5] + s[6] + s[7] + s[8] + \n" + + " s[9] + s[10] + s[11] + s[12] + s[13] + s[14] + \n" + + " s[15] + s[16] + s[17] + s[18] + s[19] + s[20] + \n" + + " s[21] + s[22] + s[23] + s[24] + s[25] + s[26] + \n" + + " s[27] + s[28] + s[29]\n" + + " );\n" + + "}\n" + + "}" + }, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +// variant involving a constant combined binary expression +public void test0011_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " final String c = \"a\";" + + " System.out.println(\n" + + " c + c + c + c + c + c + c + c + c + c + \n" + + " c + c + c + c + c + c + c + c + c + c + \n" + + " c + c + s[22] + s[23] + s[24] + s[25] + s[26] + \n" + + " s[27] + s[28] + s[29]\n" + + " );\n" + + "}\n" + + "}" + }, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// AST implementation - visiting binary expressions +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// Adding combined binary expressions - checking recursive print +public void test_0012_combined_binary_expression() { + CombinedBinaryExpression.defaultArityMaxStartingValue = 2; + runConformTest( + "X.java", + "public class X {\n" + + " void foo() {\n" + + " String s1 = \"s1\";\n" + + " String s2 = \"s2\";\n" + + " String s3 = \"s3\";\n" + + " String s4 = \"s4\";\n" + + " System.out.println(s1 + \"l1\" + s2 + \"l2\" +\n" + + " s3 + s1 + s4);\n" + + " }\n" + + "}\n", + defaultParser, + new ASTCollector() { + public boolean visit(BinaryExpression binaryExpression, + BlockScope scope) { + super.visit(binaryExpression, scope); + this.collector.append(binaryExpression); + return true; + } + }, + "((((((s1 + \"l1\") + s2) + \"l2\") + s3) + s1) + s4)(((((s1 + \"l1\")" + + " + s2) + \"l2\") + s3) + s1)((((s1 + \"l1\") + s2) + \"l2\") + s3)" + + "(((s1 + \"l1\") + s2) + \"l2\")((s1 + \"l1\") + s2)(s1 + \"l1\")"); + CombinedBinaryExpression.defaultArityMaxStartingValue = + CombinedBinaryExpression.ARITY_MAX_MIN; +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +// variant involving a left-deep right expression at the topmost level +public void test0013_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " System.out.println(\n" + + " \"b\" + (s[0] + s[1] + s[2] + s[3] + s[4] + s[5] + s[6] + \n" + + " s[7] + s[8] + s[9] + s[10] + s[11] + s[12] + s[13] +\n" + + " s[14] + s[15] + s[16] + s[17] + s[18] + s[19] + \n" + + " s[20] + s[21] + s[22] + s[23] + s[24] + s[25] + \n" + + " s[26] + s[27] + s[28] + s[29])\n" + + " );\n" + + "}\n" + + "}" + }, + "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +// variant involving a left-deep right expression at the topmost level, with +// a constant high in tree +public void test0014_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " final String c = \"c\";\n" + + " System.out.println(\n" + + " \"b\" + \n" + + " (c + c + c + c + c + c + c + c + c + c + \n" + + " c + c + c + c + c + c + c + c + c + c + \n" + + " c + c + s[0])\n" + + " );\n" + + "}\n" + + "}" + }, + "bcccccccccccccccccccccca"); +} + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102728 +// check if the generated code is OK when leveraging CombinedBinaryExpression +// variant involving a left-deep right expression at the topmost level, with +// a constant low in tree +public void test0015_combined_binary_expression() { + assertEquals(20, CombinedBinaryExpression.ARITY_MAX_MIN); + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + "public static void main(String args[]) {\n" + + " final int max = 30; \n" + + " String s[] = new String[max];\n" + + " for (int i = 0; i < max; i++) {\n" + + " s[i] = \"a\";\n" + + " }\n" + + " foo(s);\n" + + "}\n" + + "static void foo (String s[]) {\n" + + " final String c = \"c\";\n" + + " System.out.println(\n" + + " \"b\" + \n" + + " (c + c + c + c + c + c + c + c + c + c + \n" + + " c + c + c + c + c + c + c + c + c + c + \n" + + " s[0] + s[1] + s[2])\n" + + " );\n" + + "}\n" + + "}" + }, + "bccccccccccccccccccccaaa"); +} +} + +// Helper classes: define visitors leveraged by some tests +class ASTCollector extends ASTVisitor { + StringBuffer collector = new StringBuffer(); +public String result() { + return this.collector.toString(); +} +} + +class ASTBinaryExpressionCollector extends ASTCollector { +static final int LIMIT = 30; +// help limit the output in length by suppressing the middle +// part of strings which length exceeds LIMIT +String cut(String source) { + int length; + if ((length = source.length()) > LIMIT) { + StringBuffer result = new StringBuffer(length); + result.append(source.substring(0, LIMIT - 10)); + result.append("..."); + result.append(source.substring(length - 7, length)); + return result.toString(); + } else { + return source; + } +} +public void endVisit(BinaryExpression binaryExpression, BlockScope scope) { + this.collector.append("[ev BE " + cut(binaryExpression.toString()) + "]\n"); + super.endVisit(binaryExpression, scope); +} + +public void endVisit(CharLiteral charLiteral, BlockScope scope) { + this.collector.append("[ev CL " + cut(charLiteral.toString()) + "]\n"); + super.endVisit(charLiteral, scope); +} + +public void endVisit(ExtendedStringLiteral literal, BlockScope scope) { + this.collector.append("[ev ESL " + cut(literal.toString()) + "]\n"); + super.endVisit(literal, scope); +} + +public void endVisit(SingleNameReference singleNameReference, + BlockScope scope) { + this.collector.append("[ev SNR " + cut(singleNameReference.toString()) + + "]\n"); + super.endVisit(singleNameReference, scope); +} + +public void endVisit(StringLiteral stringLiteral, BlockScope scope) { + this.collector.append("[ev SL " + cut(stringLiteral.toString()) + "]\n"); + super.endVisit(stringLiteral, scope); +} + +public void endVisit(StringLiteralConcatenation literal, BlockScope scope) { + this.collector.append("[ev SLC " + cut(literal.toString()) + "]\n"); + super.endVisit(literal, scope); +} + +public boolean visit(BinaryExpression binaryExpression, BlockScope scope) { + this.collector.append("[v BE " + cut(binaryExpression.toString()) + "]\n"); + return super.visit(binaryExpression, scope); +} + +public boolean visit(CharLiteral charLiteral, BlockScope scope) { + this.collector.append("[v CL " + cut(charLiteral.toString()) + "]\n"); + return super.visit(charLiteral, scope); +} + +public boolean visit(ExtendedStringLiteral literal, BlockScope scope) { + this.collector.append("[v ESL " + cut(literal.toString()) + "]\n"); + return super.visit(literal, scope); +} + +public boolean visit(SingleNameReference singleNameReference, + BlockScope scope) { + this.collector.append("[v SNR " + cut(singleNameReference.toString()) + + "]\n"); + return super.visit(singleNameReference, scope); +} + +public boolean visit(StringLiteral stringLiteral, BlockScope scope) { + this.collector.append("[v SL " + cut(stringLiteral.toString()) + "]\n"); + return super.visit(stringLiteral, scope); +} + +public boolean visit(StringLiteralConcatenation literal, BlockScope scope) { + this.collector.append("[v SLC " + cut(literal.toString()) + "]\n"); + return super.visit(literal, scope); +} +}