1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.parser;
13 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
14 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.Argument;
16 import net.sourceforge.phpdt.internal.compiler.ast.AstNode;
17 import net.sourceforge.phpdt.internal.compiler.ast.Block;
18 import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration;
19 import net.sourceforge.phpdt.internal.compiler.ast.ExplicitConstructorCall;
20 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
21 import net.sourceforge.phpdt.internal.compiler.ast.LocalDeclaration;
22 import net.sourceforge.phpdt.internal.compiler.ast.LocalTypeDeclaration;
23 import net.sourceforge.phpdt.internal.compiler.ast.Statement;
24 import net.sourceforge.phpdt.internal.compiler.ast.SuperReference;
25 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
26 import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
27 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypes;
28 import net.sourceforge.phpdt.internal.compiler.lookup.CompilerModifiers;
29 import net.sourceforge.phpdt.internal.compiler.util.CharOperation;
32 * Internal method structure for parsing recovery
35 public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, ITerminalSymbols, BaseTypes {
37 public AbstractMethodDeclaration methodDeclaration;
39 public RecoveredType[] localTypes;
40 public int localTypeCount;
42 public RecoveredBlock methodBody;
43 public boolean discardBody = true;
45 public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
46 super(parent, bracketBalance, parser);
47 this.methodDeclaration = methodDeclaration;
48 this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
49 if(this.foundOpeningBrace) {
50 this.bracketBalance++;
54 * Record a nested block declaration
56 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
58 /* default behavior is to delegate recording to parent if any,
59 do not consider elements passed the known end (if set)
60 it must be belonging to an enclosing element
62 if (methodDeclaration.declarationSourceEnd > 0
63 && nestedBlockDeclaration.sourceStart
64 > methodDeclaration.declarationSourceEnd){
65 if (this.parent == null){
66 return this; // ignore
68 return this.parent.add(nestedBlockDeclaration, bracketBalance);
71 /* consider that if the opening brace was not found, it is there */
72 if (!foundOpeningBrace){
73 foundOpeningBrace = true;
74 this.bracketBalance++;
77 methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance);
78 if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
82 * Record a field declaration
84 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
86 /* local variables inside method can only be final and non void */
87 char[][] fieldTypeName;
88 if ((fieldDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
89 || (fieldDeclaration.type == null) // initializer
90 || ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
91 && CharOperation.equals(fieldTypeName[0], VoidBinding.sourceName()))){
93 if (this.parent == null){
94 return this; // ignore
96 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
97 return this.parent.add(fieldDeclaration, bracketBalance);
100 /* default behavior is to delegate recording to parent if any,
101 do not consider elements passed the known end (if set)
102 it must be belonging to an enclosing element
104 if (methodDeclaration.declarationSourceEnd > 0
105 && fieldDeclaration.declarationSourceStart
106 > methodDeclaration.declarationSourceEnd){
107 if (this.parent == null){
108 return this; // ignore
110 return this.parent.add(fieldDeclaration, bracketBalance);
113 /* consider that if the opening brace was not found, it is there */
114 if (!foundOpeningBrace){
115 foundOpeningBrace = true;
116 this.bracketBalance++;
118 // still inside method, treat as local variable
119 return this; // ignore
122 * Record a local declaration - regular method should have been created a block body
124 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
126 /* local variables inside method can only be final and non void */
128 char[][] localTypeName;
129 if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
130 || (localDeclaration.type == null) // initializer
131 || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
132 && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
134 if (this.parent == null){
135 return this; // ignore
137 this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
138 return this.parent.add(localDeclaration, bracketBalance);
142 /* do not consider a type starting passed the type end (if set)
143 it must be belonging to an enclosing type */
144 if (methodDeclaration.declarationSourceEnd != 0
145 && localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
147 if (this.parent == null) {
148 return this; // ignore
150 return this.parent.add(localDeclaration, bracketBalance);
153 if (methodBody == null){
154 Block block = new Block(0);
155 block.sourceStart = methodDeclaration.bodyStart;
156 RecoveredElement currentBlock = this.add(block, 1);
157 if (this.bracketBalance > 0){
158 for (int i = 0; i < this.bracketBalance - 1; i++){
159 currentBlock = currentBlock.add(new Block(0), 1);
161 this.bracketBalance = 1;
163 return currentBlock.add(localDeclaration, bracketBalance);
165 return methodBody.add(localDeclaration, bracketBalance, true);
168 * Record a statement - regular method should have been created a block body
170 public RecoveredElement add(Statement statement, int bracketBalance) {
172 /* do not consider a type starting passed the type end (if set)
173 it must be belonging to an enclosing type */
174 if (methodDeclaration.declarationSourceEnd != 0
175 && statement.sourceStart > methodDeclaration.declarationSourceEnd){
177 if (this.parent == null) {
178 return this; // ignore
180 return this.parent.add(statement, bracketBalance);
183 if (methodBody == null){
184 Block block = new Block(0);
185 block.sourceStart = methodDeclaration.bodyStart;
186 RecoveredElement currentBlock = this.add(block, 1);
187 if (this.bracketBalance > 0){
188 for (int i = 0; i < this.bracketBalance - 1; i++){
189 currentBlock = currentBlock.add(new Block(0), 1);
191 this.bracketBalance = 1;
193 return currentBlock.add(statement, bracketBalance);
195 return methodBody.add(statement, bracketBalance, true);
197 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) {
199 /* do not consider a type starting passed the type end (if set)
200 it must be belonging to an enclosing type */
201 if (methodDeclaration.declarationSourceEnd != 0
202 && typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd){
204 if (this.parent == null) {
205 return this; // ignore
207 return this.parent.add(typeDeclaration, bracketBalance);
210 if (typeDeclaration instanceof LocalTypeDeclaration){
211 if (methodBody == null){
212 Block block = new Block(0);
213 block.sourceStart = methodDeclaration.bodyStart;
216 return methodBody.add(typeDeclaration, bracketBalance, true);
218 if (localTypes == null) {
219 localTypes = new RecoveredType[5];
222 if (localTypeCount == localTypes.length) {
226 (localTypes = new RecoveredType[2 * localTypeCount]),
231 RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalance);
232 localTypes[localTypeCount++] = element;
234 /* consider that if the opening brace was not found, it is there */
235 if (!foundOpeningBrace){
236 foundOpeningBrace = true;
237 this.bracketBalance++;
241 public boolean bodyStartsAtHeaderEnd(){
242 return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
245 * Answer the associated parsed structure
247 public AstNode parseTree(){
248 return methodDeclaration;
251 * Answer the very source end of the corresponding parse node
253 public int sourceEnd(){
254 return this.methodDeclaration.declarationSourceEnd;
256 public String toString(int tab) {
257 StringBuffer result = new StringBuffer(tabString(tab));
258 result.append("Recovered method:\n"); //$NON-NLS-1$
259 result.append(this.methodDeclaration.toString(tab + 1));
260 if (this.localTypes != null) {
261 for (int i = 0; i < this.localTypeCount; i++) {
262 result.append("\n"); //$NON-NLS-1$
263 result.append(this.localTypes[i].toString(tab + 1));
266 if (this.methodBody != null) {
267 result.append("\n"); //$NON-NLS-1$
268 result.append(this.methodBody.toString(tab + 1));
270 return result.toString();
273 * Update the bodyStart of the corresponding parse node
275 public void updateBodyStart(int bodyStart){
276 this.foundOpeningBrace = true;
277 this.methodDeclaration.bodyStart = bodyStart;
279 public AbstractMethodDeclaration updatedMethodDeclaration(){
281 if (methodBody != null){
282 Block block = methodBody.updatedBlock();
284 methodDeclaration.statements = block.statements;
286 /* first statement might be an explict constructor call destinated to a special slot */
287 if (methodDeclaration.isConstructor()) {
288 ConstructorDeclaration constructor = (ConstructorDeclaration)methodDeclaration;
289 if (methodDeclaration.statements != null
290 && methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
291 constructor.constructorCall = (ExplicitConstructorCall)methodDeclaration.statements[0];
292 int length = methodDeclaration.statements.length;
294 methodDeclaration.statements,
296 (methodDeclaration.statements = new Statement[length-1]),
300 if (constructor.constructorCall == null){ // add implicit constructor call
301 constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
306 if (localTypeCount > 0) methodDeclaration.bits |= AstNode.HasLocalTypeMASK;
307 return methodDeclaration;
310 * Update the corresponding parse node from parser state which
311 * is about to disappear because of restarting recovery
313 public void updateFromParserState(){
315 if(this.bodyStartsAtHeaderEnd()){
316 Parser parser = this.parser();
317 /* might want to recover arguments or thrown exceptions */
318 if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
319 /* has consumed the arguments - listed elements must be thrown exceptions */
320 if (methodDeclaration.sourceEnd == parser.rParenPos) {
322 // protection for bugs 15142
323 int length = parser.astLengthStack[parser.astLengthPtr];
324 int astPtr = parser.astPtr - length;
325 boolean canConsume = astPtr >= 0;
327 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
330 for (int i = 1, max = length + 1; i < max; i++) {
331 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
337 parser.consumeMethodHeaderThrowsClause();
338 // will reset typeListLength to zero
339 // thus this check will only be performed on first errorCheck after void foo() throws X, Y,
341 parser.listLength = 0;
344 /* has not consumed arguments yet, listed elements must be arguments */
345 if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
346 /* if currentToken is parenthesis this last argument is a method/field signature */
347 parser.astLengthStack[parser.astLengthPtr] --;
349 parser.listLength --;
350 parser.currentToken = 0;
352 int argLength = parser.astLengthStack[parser.astLengthPtr];
353 int argStart = parser.astPtr - argLength + 1;
354 boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
355 // to compute bodyStart, and thus used to set next checkpoint.
357 for (count = 0; count < argLength; count++){
358 Argument argument = (Argument)parser.astStack[argStart+count];
359 /* cannot be an argument if non final */
360 char[][] argTypeName = argument.type.getTypeName();
361 if ((argument.modifiers & ~AccFinal) != 0
362 || (argTypeName.length == 1
363 && CharOperation.equals(argTypeName[0], VoidBinding.sourceName()))){
364 parser.astLengthStack[parser.astLengthPtr] = count;
365 parser.astPtr = argStart+count-1;
366 parser.listLength = count;
367 parser.currentToken = 0;
370 if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
372 if (parser.listLength > 0 && parser.astLengthPtr > 0){
374 // protection for bugs 15142
375 int length = parser.astLengthStack[parser.astLengthPtr];
376 int astPtr = parser.astPtr - length;
377 boolean canConsume = astPtr >= 0;
379 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
382 for (int i = 1, max = length + 1; i < max; i++) {
383 if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
389 parser.consumeMethodHeaderParameters();
390 /* fix-up positions, given they were updated against rParenPos, which did not get set */
391 if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
392 methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length-1].sourceEnd;
393 methodDeclaration.bodyStart = methodDeclaration.sourceEnd+1;
394 parser.lastCheckPoint = methodDeclaration.bodyStart;
403 * An opening brace got consumed, might be the expected opening one of the current element,
404 * in which case the bodyStart is updated.
406 public RecoveredElement updateOnOpeningBrace(int braceEnd){
408 /* in case the opening brace is close enough to the signature */
409 if (bracketBalance == 0){
411 if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
412 != parser.scanner.searchLineNumber(braceEnd)){
414 switch(parser().lastIgnoredToken){
416 // case TokenNamethrows :
419 this.foundOpeningBrace = true;
420 bracketBalance = 1; // pretend the brace was already there
423 return super.updateOnOpeningBrace(braceEnd);
425 public void updateParseTree(){
426 this.updatedMethodDeclaration();
429 * Update the declarationSourceEnd of the corresponding parse node
431 public void updateSourceEndIfNecessary(int sourceEnd){
432 if (this.methodDeclaration.declarationSourceEnd == 0) {
433 this.methodDeclaration.declarationSourceEnd = sourceEnd;
434 this.methodDeclaration.bodyEnd = sourceEnd;