blob: 98aab657cd865f1b11dfa6d83f35f7cd8cd66d78 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypes;
import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
/**
* Internal method structure for parsing recovery
*/
public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, TerminalTokens, BaseTypes {
public AbstractMethodDeclaration methodDeclaration;
public RecoveredType[] localTypes;
public int localTypeCount;
public RecoveredBlock methodBody;
public boolean discardBody = true;
public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
super(parent, bracketBalance, parser);
this.methodDeclaration = methodDeclaration;
this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
if(this.foundOpeningBrace) {
this.bracketBalance++;
}
}
/*
* Record a nested block declaration
*/
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
/* default behavior is to delegate recording to parent if any,
do not consider elements passed the known end (if set)
it must be belonging to an enclosing element
*/
if (methodDeclaration.declarationSourceEnd > 0
&& nestedBlockDeclaration.sourceStart
> methodDeclaration.declarationSourceEnd){
if (this.parent == null){
return this; // ignore
} else {
return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
}
}
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
return this;
}
/*
* Record a field declaration
*/
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
/* local variables inside method can only be final and non void */
char[][] fieldTypeName;
if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
|| (fieldDeclaration.type == null) // initializer
|| ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
if (this.parent == null){
return this; // ignore
} else {
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
}
/* default behavior is to delegate recording to parent if any,
do not consider elements passed the known end (if set)
it must be belonging to an enclosing element
*/
if (methodDeclaration.declarationSourceEnd > 0
&& fieldDeclaration.declarationSourceStart
> methodDeclaration.declarationSourceEnd){
if (this.parent == null){
return this; // ignore
} else {
return this.parent.add(fieldDeclaration, bracketBalanceValue);
}
}
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
// still inside method, treat as local variable
return this; // ignore
}
/*
* Record a local declaration - regular method should have been created a block body
*/
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
/* local variables inside method can only be final and non void */
/*
char[][] localTypeName;
if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
|| (localDeclaration.type == null) // initializer
|| ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
&& CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
if (this.parent == null){
return this; // ignore
} else {
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
return this.parent.add(localDeclaration, bracketBalance);
}
}
*/
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
if (this.parent == null) {
return this; // ignore
} else {
return this.parent.add(localDeclaration, bracketBalanceValue);
}
}
if (methodBody == null){
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
RecoveredElement currentBlock = this.add(block, 1);
if (this.bracketBalance > 0){
for (int i = 0; i < this.bracketBalance - 1; i++){
currentBlock = currentBlock.add(new Block(0), 1);
}
this.bracketBalance = 1;
}
return currentBlock.add(localDeclaration, bracketBalanceValue);
}
return methodBody.add(localDeclaration, bracketBalanceValue, true);
}
/*
* Record a statement - regular method should have been created a block body
*/
public RecoveredElement add(Statement statement, int bracketBalanceValue) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& statement.sourceStart > methodDeclaration.declarationSourceEnd){
if (this.parent == null) {
return this; // ignore
} else {
return this.parent.add(statement, bracketBalanceValue);
}
}
if (methodBody == null){
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
RecoveredElement currentBlock = this.add(block, 1);
if (this.bracketBalance > 0){
for (int i = 0; i < this.bracketBalance - 1; i++){
currentBlock = currentBlock.add(new Block(0), 1);
}
this.bracketBalance = 1;
}
return currentBlock.add(statement, bracketBalanceValue);
}
return methodBody.add(statement, bracketBalanceValue, true);
}
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {
/* do not consider a type starting passed the type end (if set)
it must be belonging to an enclosing type */
if (methodDeclaration.declarationSourceEnd != 0
&& typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
if (this.parent == null) {
return this; // ignore
}
return this.parent.add(typeDeclaration, bracketBalanceValue);
}
if ((typeDeclaration.bits & ASTNode.IsLocalTypeMASK) != 0){
if (methodBody == null){
Block block = new Block(0);
block.sourceStart = methodDeclaration.bodyStart;
this.add(block, 1);
}
return methodBody.add(typeDeclaration, bracketBalanceValue, true);
}
if (typeDeclaration.isInterface()) {
this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
if (this.parent == null) {
return this; // ignore
}
// close the constructor
return this.parent.add(typeDeclaration, bracketBalanceValue);
}
if (localTypes == null) {
localTypes = new RecoveredType[5];
localTypeCount = 0;
} else {
if (localTypeCount == localTypes.length) {
System.arraycopy(
localTypes,
0,
(localTypes = new RecoveredType[2 * localTypeCount]),
0,
localTypeCount);
}
}
RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
localTypes[localTypeCount++] = element;
/* consider that if the opening brace was not found, it is there */
if (!foundOpeningBrace){
foundOpeningBrace = true;
this.bracketBalance++;
}
return element;
}
public boolean bodyStartsAtHeaderEnd(){
return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
}
/*
* Answer the associated parsed structure
*/
public ASTNode parseTree(){
return methodDeclaration;
}
/*
* Answer the very source end of the corresponding parse node
*/
public int sourceEnd(){
return this.methodDeclaration.declarationSourceEnd;
}
public String toString(int tab) {
StringBuffer result = new StringBuffer(tabString(tab));
result.append("Recovered method:\n"); //$NON-NLS-1$
this.methodDeclaration.print(tab + 1, result);
if (this.localTypes != null) {
for (int i = 0; i < this.localTypeCount; i++) {
result.append("\n"); //$NON-NLS-1$
result.append(this.localTypes[i].toString(tab + 1));
}
}
if (this.methodBody != null) {
result.append("\n"); //$NON-NLS-1$
result.append(this.methodBody.toString(tab + 1));
}
return result.toString();
}
/*
* Update the bodyStart of the corresponding parse node
*/
public void updateBodyStart(int bodyStart){
this.foundOpeningBrace = true;
this.methodDeclaration.bodyStart = bodyStart;
}
public AbstractMethodDeclaration updatedMethodDeclaration(){
if (methodBody != null){
Block block = methodBody.updatedBlock();
if (block != null){
methodDeclaration.statements = block.statements;
/* first statement might be an explict constructor call destinated to a special slot */
if (methodDeclaration.isConstructor()) {
ConstructorDeclaration constructor = (ConstructorDeclaration)methodDeclaration;
if (methodDeclaration.statements != null
&& methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
constructor.constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
int length = methodDeclaration.statements.length;
System.arraycopy(
methodDeclaration.statements,
1,
(methodDeclaration.statements = new Statement[length-1]),
0,
length-1);
}
if (constructor.constructorCall == null){ // add implicit constructor call
constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
}
}
}
}
if (localTypeCount > 0) methodDeclaration.bits |= ASTNode.HasLocalTypeMASK;
return methodDeclaration;
}
/*
* Update the corresponding parse node from parser state which
* is about to disappear because of restarting recovery
*/
public void updateFromParserState(){
if(this.bodyStartsAtHeaderEnd()){
Parser parser = this.parser();
/* might want to recover arguments or thrown exceptions */
if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
/* has consumed the arguments - listed elements must be thrown exceptions */
if (methodDeclaration.sourceEnd == parser.rParenPos) {
// protection for bugs 15142
int length = parser.astLengthStack[parser.astLengthPtr];
int astPtr = parser.astPtr - length;
boolean canConsume = astPtr >= 0;
if(canConsume) {
if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
canConsume = false;
}
for (int i = 1, max = length + 1; i < max; i++) {
if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
canConsume = false;
}
}
}
if (canConsume){
parser.consumeMethodHeaderThrowsClause();
// will reset typeListLength to zero
// thus this check will only be performed on first errorCheck after void foo() throws X, Y,
} else {
parser.listLength = 0;
}
} else {
/* has not consumed arguments yet, listed elements must be arguments */
if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
/* if currentToken is parenthesis this last argument is a method/field signature */
parser.astLengthStack[parser.astLengthPtr] --;
parser.astPtr --;
parser.listLength --;
parser.currentToken = 0;
}
int argLength = parser.astLengthStack[parser.astLengthPtr];
int argStart = parser.astPtr - argLength + 1;
boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
// to compute bodyStart, and thus used to set next checkpoint.
int count;
for (count = 0; count < argLength; count++){
Argument argument = (Argument)parser.astStack[argStart+count];
/* cannot be an argument if non final */
char[][] argTypeName = argument.type.getTypeName();
if ((argument.modifiers & ~AccFinal) != 0
|| (argTypeName.length == 1
&& CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
parser.astLengthStack[parser.astLengthPtr] = count;
parser.astPtr = argStart+count-1;
parser.listLength = count;
parser.currentToken = 0;
break;
}
if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
}
if (parser.listLength > 0 && parser.astLengthPtr > 0){
// protection for bugs 15142
int length = parser.astLengthStack[parser.astLengthPtr];
int astPtr = parser.astPtr - length;
boolean canConsume = astPtr >= 0;
if(canConsume) {
if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
canConsume = false;
}
for (int i = 1, max = length + 1; i < max; i++) {
if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
canConsume = false;
}
}
}
if(canConsume) {
parser.consumeMethodHeaderRightParen();
/* fix-up positions, given they were updated against rParenPos, which did not get set */
if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
parser.lastCheckPoint = methodDeclaration.bodyStart;
}
}
}
}
}
}
}
/*
* An opening brace got consumed, might be the expected opening one of the current element,
* in which case the bodyStart is updated.
*/
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){
/* in case the opening brace is close enough to the signature */
if (bracketBalance == 0){
/*
if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
!= parser.scanner.searchLineNumber(braceEnd)){
*/
switch(parser().lastIgnoredToken){
case -1 :
case TokenNamethrows :
break;
default:
this.foundOpeningBrace = true;
bracketBalance = 1; // pretend the brace was already there
}
}
return super.updateOnOpeningBrace(braceStart, braceEnd);
}
public void updateParseTree(){
this.updatedMethodDeclaration();
}
/*
* Update the declarationSourceEnd of the corresponding parse node
*/
public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
if (this.methodDeclaration.declarationSourceEnd == 0) {
if(parser().rBraceSuccessorStart >= braceEnd) {
this.methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
this.methodDeclaration.bodyEnd = parser().rBraceStart;
} else {
this.methodDeclaration.declarationSourceEnd = braceEnd;
this.methodDeclaration.bodyEnd = braceStart - 1;
}
}
}
}