/******************************************************************************* * 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.env.IGenericType; 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.kind() == IGenericType.INTERFACE_DECL) { 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; } } } } } } } 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; } 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 (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; } } } }