/******************************************************************************* * Copyright (c) 2000, 2009 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.parser; import java.util.HashSet; import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; 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.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; 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.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.util.Util; /** * Internal method structure for parsing recovery */ public class RecoveredMethod extends RecoveredElement implements TerminalTokens { public AbstractMethodDeclaration methodDeclaration; public RecoveredAnnotation[] annotations; public int annotationCount; public int modifiers; public int modifiersStart; public RecoveredType[] localTypes; public int localTypeCount; public RecoveredBlock methodBody; public boolean discardBody= true; int pendingModifiers; int pendingModifersSourceStart= -1; RecoveredAnnotation[] pendingAnnotations; int pendingAnnotationCount; 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 (this.methodDeclaration.declarationSourceEnd > 0 && nestedBlockDeclaration.sourceStart > this.methodDeclaration.declarationSourceEnd) { resetPendingModifiers(); 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 (!this.foundOpeningBrace) { this.foundOpeningBrace= true; this.bracketBalance++; } this.methodBody= new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); if (nestedBlockDeclaration.sourceEnd == 0) return this.methodBody; return this; } /* * Record a field declaration */ public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) { resetPendingModifiers(); /* local variables inside method can only be final and non void */ char[][] fieldTypeName; if ((fieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 // local var can only be final || (fieldDeclaration.type == null) // initializer || ((fieldTypeName= fieldDeclaration.type.getTypeName()).length == 1 // non void && CharOperation.equals(fieldTypeName[0], TypeBinding.VOID.sourceName()))) { if (this.parent == null) { return this; // ignore } else { this.updateSourceEndIfNecessary(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 (this.methodDeclaration.declarationSourceEnd > 0 && fieldDeclaration.declarationSourceStart > this.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 (!this.foundOpeningBrace) { this.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) { resetPendingModifiers(); /* 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 (this.methodDeclaration.declarationSourceEnd != 0 && localDeclaration.declarationSourceStart > this.methodDeclaration.declarationSourceEnd) { if (this.parent == null) { return this; // ignore } else { return this.parent.add(localDeclaration, bracketBalanceValue); } } if (this.methodBody == null) { Block block= new Block(0); block.sourceStart= this.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 this.methodBody.add(localDeclaration, bracketBalanceValue, true); } /* * Record a statement - regular method should have been created a block body */ public RecoveredElement add(Statement statement, int bracketBalanceValue) { resetPendingModifiers(); /* do not consider a type starting passed the type end (if set) it must be belonging to an enclosing type */ if (this.methodDeclaration.declarationSourceEnd != 0 && statement.sourceStart > this.methodDeclaration.declarationSourceEnd) { if (this.parent == null) { return this; // ignore } else { return this.parent.add(statement, bracketBalanceValue); } } if (this.methodBody == null) { Block block= new Block(0); block.sourceStart= this.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 this.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 (this.methodDeclaration.declarationSourceEnd != 0 && typeDeclaration.declarationSourceStart > this.methodDeclaration.declarationSourceEnd) { if (this.parent == null) { return this; // ignore } return this.parent.add(typeDeclaration, bracketBalanceValue); } if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0 || parser().methodRecoveryActivated || parser().statementRecoveryActivated) { if (this.methodBody == null) { Block block= new Block(0); block.sourceStart= this.methodDeclaration.bodyStart; this.add(block, 1); } this.methodBody.attachPendingModifiers( this.pendingAnnotations, this.pendingAnnotationCount, this.pendingModifiers, this.pendingModifersSourceStart); resetPendingModifiers(); return this.methodBody.add(typeDeclaration, bracketBalanceValue, true); } switch (TypeDeclaration.kind(typeDeclaration.modifiers)) { case TypeDeclaration.INTERFACE_DECL: case TypeDeclaration.ANNOTATION_TYPE_DECL: resetPendingModifiers(); this.updateSourceEndIfNecessary(previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1)); if (this.parent == null) { return this; // ignore } // close the constructor return this.parent.add(typeDeclaration, bracketBalanceValue); } if (this.localTypes == null) { this.localTypes= new RecoveredType[5]; this.localTypeCount= 0; } else { if (this.localTypeCount == this.localTypes.length) { System.arraycopy( this.localTypes, 0, (this.localTypes= new RecoveredType[2 * this.localTypeCount]), 0, this.localTypeCount); } } RecoveredType element= new RecoveredType(typeDeclaration, this, bracketBalanceValue); this.localTypes[this.localTypeCount++]= element; if (this.pendingAnnotationCount > 0) { element.attach( this.pendingAnnotations, this.pendingAnnotationCount, this.pendingModifiers, this.pendingModifersSourceStart); } resetPendingModifiers(); /* consider that if the opening brace was not found, it is there */ if (!this.foundOpeningBrace) { this.foundOpeningBrace= true; this.bracketBalance++; } return element; } public boolean bodyStartsAtHeaderEnd() { return this.methodDeclaration.bodyStart == this.methodDeclaration.sourceEnd + 1; } /* * Answer the associated parsed structure */ public ASTNode parseTree() { return this.methodDeclaration; } public void resetPendingModifiers() { this.pendingAnnotations= null; this.pendingAnnotationCount= 0; this.pendingModifiers= 0; this.pendingModifersSourceStart= -1; } /* * 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.annotations != null) { for (int i= 0; i < this.annotationCount; i++) { result.append("\n"); //$NON-NLS-1$ result.append(this.annotations[i].toString(tab + 1)); } } 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(int depth, Set knownTypes) { /* update annotations */ if (this.modifiers != 0) { this.methodDeclaration.modifiers|= this.modifiers; if (this.modifiersStart < this.methodDeclaration.declarationSourceStart) { this.methodDeclaration.declarationSourceStart= this.modifiersStart; } } /* update annotations */ if (this.annotationCount > 0) { int existingCount= this.methodDeclaration.annotations == null ? 0 : this.methodDeclaration.annotations.length; Annotation[] annotationReferences= new Annotation[existingCount + this.annotationCount]; if (existingCount > 0) { System.arraycopy(this.methodDeclaration.annotations, 0, annotationReferences, this.annotationCount, existingCount); } for (int i= 0; i < this.annotationCount; i++) { annotationReferences[i]= this.annotations[i].updatedAnnotationReference(); } this.methodDeclaration.annotations= annotationReferences; int start= this.annotations[0].annotation.sourceStart; if (start < this.methodDeclaration.declarationSourceStart) { this.methodDeclaration.declarationSourceStart= start; } } if (this.methodBody != null) { Block block= this.methodBody.updatedBlock(depth, knownTypes); if (block != null) { this.methodDeclaration.statements= block.statements; if (this.methodDeclaration.declarationSourceEnd == 0) { this.methodDeclaration.declarationSourceEnd= block.sourceEnd; this.methodDeclaration.bodyEnd= block.sourceEnd; } /* first statement might be an explict constructor call destinated to a special slot */ if (this.methodDeclaration.isConstructor()) { ConstructorDeclaration constructor= (ConstructorDeclaration)this.methodDeclaration; if (this.methodDeclaration.statements != null && this.methodDeclaration.statements[0] instanceof ExplicitConstructorCall) { constructor.constructorCall= (ExplicitConstructorCall)this.methodDeclaration.statements[0]; int length= this.methodDeclaration.statements.length; System.arraycopy( this.methodDeclaration.statements, 1, (this.methodDeclaration.statements= new Statement[length - 1]), 0, length - 1); } if (constructor.constructorCall == null) { // add implicit constructor call constructor.constructorCall= SuperReference.implicitSuperConstructorCall(); } } } } else { if (this.methodDeclaration.declarationSourceEnd == 0) { if (this.methodDeclaration.sourceEnd + 1 == this.methodDeclaration.bodyStart) { // right brace is missing this.methodDeclaration.declarationSourceEnd= this.methodDeclaration.sourceEnd; this.methodDeclaration.bodyStart= this.methodDeclaration.sourceEnd; this.methodDeclaration.bodyEnd= this.methodDeclaration.sourceEnd; } else { this.methodDeclaration.declarationSourceEnd= this.methodDeclaration.bodyStart; this.methodDeclaration.bodyEnd= this.methodDeclaration.bodyStart; } } } if (this.localTypeCount > 0) this.methodDeclaration.bits|= ASTNode.HasLocalType; return this.methodDeclaration; } /* * Update the corresponding parse node from parser state which * is about to disappear because of restarting recovery */ public void updateFromParserState() { // if parent is null then recovery already occured in diet parser. if (bodyStartsAtHeaderEnd() && this.parent != null) { Parser parser= 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 (this.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 // remove unfinished annotation nodes MemberValuePair[] memberValuePairs= null; while (argLength > 0 && parser.astStack[parser.astPtr] instanceof MemberValuePair) { System.arraycopy(parser.astStack, argStart, memberValuePairs= new MemberValuePair[argLength], 0, argLength); parser.astLengthPtr--; parser.astPtr-= argLength; argLength= parser.astLengthStack[parser.astLengthPtr]; argStart= parser.astPtr - argLength + 1; needUpdateRParenPos= true; } // to compute bodyStart, and thus used to set next checkpoint. int count; for (count= 0; count < argLength; count++) { ASTNode aNode= parser.astStack[argStart + count]; if (aNode instanceof Argument) { Argument argument= (Argument)aNode; /* cannot be an argument if non final */ char[][] argTypeName= argument.type.getTypeName(); if ((argument.modifiers & ~ClassFileConstants.AccFinal) != 0 || (argTypeName.length == 1 && CharOperation.equals(argTypeName[0], TypeBinding.VOID.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; } else { parser.astLengthStack[parser.astLengthPtr]= count; parser.astPtr= argStart + count - 1; parser.listLength= count; parser.currentToken= 0; break; } } 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 */ this.methodDeclaration.sourceEnd= this.methodDeclaration.arguments[this.methodDeclaration.arguments.length - 1].sourceEnd; this.methodDeclaration.bodyStart= this.methodDeclaration.sourceEnd + 1; parser.lastCheckPoint= this.methodDeclaration.bodyStart; } } } if (memberValuePairs != null) { System.arraycopy(memberValuePairs, 0, parser.astStack, parser.astPtr + 1, memberValuePairs.length); parser.astPtr+= memberValuePairs.length; parser.astLengthStack[++parser.astLengthPtr]= memberValuePairs.length; } } } } } public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd) { if (this.methodDeclaration.isAnnotationMethod()) { this.updateSourceEndIfNecessary(braceStart, braceEnd); if (!this.foundOpeningBrace && this.parent != null) { return this.parent.updateOnClosingBrace(braceStart, braceEnd); } return this; } if (this.parent != null && this.parent instanceof RecoveredType) { int mods= ((RecoveredType)this.parent).typeDeclaration.modifiers; if (TypeDeclaration.kind(mods) == TypeDeclaration.INTERFACE_DECL) { if (!this.foundOpeningBrace) { this.updateSourceEndIfNecessary(braceStart - 1, braceStart - 1); return this.parent.updateOnClosingBrace(braceStart, braceEnd); } } } return super.updateOnClosingBrace(braceStart, braceEnd); } /* * 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 (this.bracketBalance == 0) { /* if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd) != parser.scanner.searchLineNumber(braceEnd)){ */ switch (parser().lastIgnoredToken) { case -1: case TokenNamethrows: break; default: this.foundOpeningBrace= true; this.bracketBalance= 1; // pretend the brace was already there } } return super.updateOnOpeningBrace(braceStart, braceEnd); } public void updateParseTree() { updatedMethodDeclaration(0, new HashSet()); } /* * 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; } } } public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengthPtr, int annotationStart, int bracketBalanceValue) { if (this.pendingAnnotations == null) { this.pendingAnnotations= new RecoveredAnnotation[5]; this.pendingAnnotationCount= 0; } else { if (this.pendingAnnotationCount == this.pendingAnnotations.length) { System.arraycopy( this.pendingAnnotations, 0, (this.pendingAnnotations= new RecoveredAnnotation[2 * this.pendingAnnotationCount]), 0, this.pendingAnnotationCount); } } RecoveredAnnotation element= new RecoveredAnnotation(identifierPtr, identifierLengthPtr, annotationStart, this, bracketBalanceValue); this.pendingAnnotations[this.pendingAnnotationCount++]= element; return element; } public void addModifier(int flag, int modifiersSourceStart) { this.pendingModifiers|= flag; if (this.pendingModifersSourceStart < 0) { this.pendingModifersSourceStart= modifiersSourceStart; } } void attach(TypeParameter[] parameters, int startPos) { if (this.methodDeclaration.modifiers != ClassFileConstants.AccDefault) return; int lastParameterEnd= parameters[parameters.length - 1].sourceEnd; Parser parser= parser(); Scanner scanner= parser.scanner; if (Util.getLineNumber(this.methodDeclaration.declarationSourceStart, scanner.lineEnds, 0, scanner.linePtr) != Util.getLineNumber(lastParameterEnd, scanner.lineEnds, 0, scanner.linePtr)) return; if (parser.modifiersSourceStart > lastParameterEnd && parser.modifiersSourceStart < this.methodDeclaration.declarationSourceStart) return; if (this.methodDeclaration instanceof MethodDeclaration) { ((MethodDeclaration)this.methodDeclaration).typeParameters= parameters; this.methodDeclaration.declarationSourceStart= startPos; } else if (this.methodDeclaration instanceof ConstructorDeclaration) { ((ConstructorDeclaration)this.methodDeclaration).typeParameters= parameters; this.methodDeclaration.declarationSourceStart= startPos; } } public void attach(RecoveredAnnotation[] annots, int annotCount, int mods, int modsSourceStart) { if (annotCount > 0) { Annotation[] existingAnnotations= this.methodDeclaration.annotations; if (existingAnnotations != null) { this.annotations= new RecoveredAnnotation[annotCount]; this.annotationCount= 0; next: for (int i= 0; i < annotCount; i++) { for (int j= 0; j < existingAnnotations.length; j++) { if (annots[i].annotation == existingAnnotations[j]) continue next; } this.annotations[this.annotationCount++]= annots[i]; } } else { this.annotations= annots; this.annotationCount= annotCount; } } if (mods != 0) { this.modifiers= mods; this.modifiersStart= modsSourceStart; } } }