/******************************************************************************* * Copyright (c) 2006, 2008 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.che.ide.ext.java.jdt.internal.compiler.ast; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ASTVisitor; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.FlowContext; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.che.ide.ext.java.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.<br> * Notes: * <ul> * <li>CombinedBinaryExpression is not meant to behave in other ways than * BinaryExpression in any observable respect;</li> * <li>visitors that implement their own traversal upon binary expressions * should consider taking advantage of combined binary expressions, or * else face a risk of StackOverflowError upon deep instances;</li> * <li>callers that need to change the operator should rebuild the expression * from scratch, or else amend the references table as needed to cope with * the resulting, separated expressions.</li> * </ul> */ public class CombinedBinaryExpression extends BinaryExpression { /** * The number of consecutive binary expressions of this' left branch that * bear the same operator as this.<br> * Notes: * <ul><li>the presence of a CombinedBinaryExpression instance resets * arity, even when its operator is compatible;</li> * <li>this property is maintained by the parser.</li> * </ul> */ 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); initArity(left, arity); } public CombinedBinaryExpression(CombinedBinaryExpression expression) { super(expression); initArity(expression.left, expression.arity); } 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, int typeID) { // // keep implementation in sync with BinaryExpression and Expression // // #generateOptimizedStringConcatenation // if (this.referencesTable == null) { // super.generateOptimizedStringConcatenation(blockScope, // typeID); // } else { // if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == // OperatorIds.PLUS) // && ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) { // if (this.constant == Constant.NotAConstant) { // BinaryExpression cursor = this.referencesTable[0]; // // int restart = 0; // 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, int typeID) { // // keep implementation in sync with BinaryExpression // // #generateOptimizedStringConcatenationCreation // if (this.referencesTable == null) { // super.generateOptimizedStringConcatenationCreation(blockScope, // 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.<init>(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, // 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.generateOptimizedStringConcatenationCreation(blockScope, // codeStream, typeID); // } // } } private void initArity(Expression expression, int value) { this.arity = value; if (value > 1) { this.referencesTable = new BinaryExpression[value]; this.referencesTable[value - 1] = (BinaryExpression)expression; for (int i = value - 1; i > 0; i--) { this.referencesTable[i - 1] = (BinaryExpression)this.referencesTable[i].left; } } else { this.arityMax = defaultArityMaxStartingValue; } } 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; } } }