/*******************************************************************************
* 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;
}
}
}