A variable description (like PHPFunctionDeclaration)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / RecoveredMethod.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.parser;
12
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;
30
31 /**
32  * Internal method structure for parsing recovery 
33  */
34
35 public class RecoveredMethod extends RecoveredElement implements CompilerModifiers, ITerminalSymbols, BaseTypes {
36
37         public AbstractMethodDeclaration methodDeclaration;
38
39         public RecoveredType[] localTypes;
40         public int localTypeCount;
41
42         public RecoveredBlock methodBody;
43         public boolean discardBody = true;
44
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++;
51         }
52 }
53 /*
54  * Record a nested block declaration
55  */
56 public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalance) {
57
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 
61         */
62         if (methodDeclaration.declarationSourceEnd > 0
63                 && nestedBlockDeclaration.sourceStart
64                         > methodDeclaration.declarationSourceEnd){
65                                 if (this.parent == null){
66                                         return this; // ignore
67                                 } else {
68                                         return this.parent.add(nestedBlockDeclaration, bracketBalance);
69                                 }
70         }
71         /* consider that if the opening brace was not found, it is there */
72         if (!foundOpeningBrace){
73                 foundOpeningBrace = true;
74                 this.bracketBalance++;
75         }
76
77         methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalance);
78         if (nestedBlockDeclaration.sourceEnd == 0) return methodBody;
79         return this;
80 }
81 /*
82  * Record a field declaration
83  */
84 public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalance) {
85
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()))){ 
92
93                 if (this.parent == null){
94                         return this; // ignore
95                 } else {
96                         this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
97                         return this.parent.add(fieldDeclaration, bracketBalance);
98                 }
99         }
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 
103         */
104         if (methodDeclaration.declarationSourceEnd > 0
105                 && fieldDeclaration.declarationSourceStart
106                         > methodDeclaration.declarationSourceEnd){
107                 if (this.parent == null){
108                         return this; // ignore
109                 } else {
110                         return this.parent.add(fieldDeclaration, bracketBalance);
111                 }
112         }
113         /* consider that if the opening brace was not found, it is there */
114         if (!foundOpeningBrace){
115                 foundOpeningBrace = true;
116                 this.bracketBalance++;
117         }
118         // still inside method, treat as local variable
119         return this; // ignore
120 }
121 /*
122  * Record a local declaration - regular method should have been created a block body
123  */
124 public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalance) {
125
126         /* local variables inside method can only be final and non void */
127 /*      
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()))){ 
133
134                 if (this.parent == null){
135                         return this; // ignore
136                 } else {
137                         this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
138                         return this.parent.add(localDeclaration, bracketBalance);
139                 }
140         }
141 */
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){
146                         
147                 if (this.parent == null) {
148                         return this; // ignore
149                 } else {
150                         return this.parent.add(localDeclaration, bracketBalance);
151                 }
152         }
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);
160                         }
161                         this.bracketBalance = 1;
162                 }
163                 return currentBlock.add(localDeclaration, bracketBalance);
164         }
165         return methodBody.add(localDeclaration, bracketBalance, true);
166 }
167 /*
168  * Record a statement - regular method should have been created a block body
169  */
170 public RecoveredElement add(Statement statement, int bracketBalance) {
171
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){
176
177                 if (this.parent == null) {
178                         return this; // ignore
179                 } else {
180                         return this.parent.add(statement, bracketBalance);
181                 }
182         }
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);
190                         }
191                         this.bracketBalance = 1;
192                 }
193                 return currentBlock.add(statement, bracketBalance);
194         }
195         return methodBody.add(statement, bracketBalance, true); 
196 }
197 public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalance) {
198
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){
203                         
204                 if (this.parent == null) {
205                         return this; // ignore
206                 } else {
207                         return this.parent.add(typeDeclaration, bracketBalance);
208                 }
209         }
210         if (typeDeclaration instanceof LocalTypeDeclaration){
211                 if (methodBody == null){
212                         Block block = new Block(0);
213                         block.sourceStart = methodDeclaration.bodyStart;
214                         this.add(block, 1);
215                 }
216                 return methodBody.add(typeDeclaration, bracketBalance, true);   
217         }       
218         if (localTypes == null) {
219                 localTypes = new RecoveredType[5];
220                 localTypeCount = 0;
221         } else {
222                 if (localTypeCount == localTypes.length) {
223                         System.arraycopy(
224                                 localTypes, 
225                                 0, 
226                                 (localTypes = new RecoveredType[2 * localTypeCount]), 
227                                 0, 
228                                 localTypeCount); 
229                 }
230         }
231         RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalance);
232         localTypes[localTypeCount++] = element;
233
234         /* consider that if the opening brace was not found, it is there */
235         if (!foundOpeningBrace){
236                 foundOpeningBrace = true;
237                 this.bracketBalance++;
238         }
239         return element;
240 }
241 public boolean bodyStartsAtHeaderEnd(){
242         return methodDeclaration.bodyStart == methodDeclaration.sourceEnd+1;
243 }
244 /* 
245  * Answer the associated parsed structure
246  */
247 public AstNode parseTree(){
248         return methodDeclaration;
249 }
250 /*
251  * Answer the very source end of the corresponding parse node
252  */
253 public int sourceEnd(){
254         return this.methodDeclaration.declarationSourceEnd;
255 }
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));
264                 }
265         }
266         if (this.methodBody != null) {
267                 result.append("\n"); //$NON-NLS-1$
268                 result.append(this.methodBody.toString(tab + 1));
269         }
270         return result.toString();
271 }
272 /*
273  * Update the bodyStart of the corresponding parse node
274  */
275 public void updateBodyStart(int bodyStart){
276         this.foundOpeningBrace = true;          
277         this.methodDeclaration.bodyStart = bodyStart;
278 }
279 public AbstractMethodDeclaration updatedMethodDeclaration(){
280
281         if (methodBody != null){
282                 Block block = methodBody.updatedBlock();
283                 if (block != null){
284                         methodDeclaration.statements = block.statements;
285
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;
293                                         System.arraycopy(
294                                                 methodDeclaration.statements, 
295                                                 1, 
296                                                 (methodDeclaration.statements = new Statement[length-1]),
297                                                 0,
298                                                 length-1);
299                                         }
300                                         if (constructor.constructorCall == null){ // add implicit constructor call
301                                                 constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
302                                         }
303                         }
304                 }
305         }
306         if (localTypeCount > 0) methodDeclaration.bits |= AstNode.HasLocalTypeMASK;
307         return methodDeclaration;
308 }
309 /*
310  * Update the corresponding parse node from parser state which
311  * is about to disappear because of restarting recovery
312  */
313 public void updateFromParserState(){
314
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) {
321                                 
322                                 // protection for bugs 15142
323                                 int length = parser.astLengthStack[parser.astLengthPtr];
324                                 int astPtr = parser.astPtr - length;
325                                 boolean canConsume = astPtr >= 0;
326                                 if(canConsume) {
327                                         if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
328                                                 canConsume = false;
329                                         }
330                                         for (int i = 1, max = length + 1; i < max; i++) {
331                                                 if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
332                                                         canConsume = false;
333                                                 }
334                                         }
335                                 }
336                                 if (canConsume){
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,
340                                 } else {
341                                         parser.listLength = 0;
342                                 }
343                         } else {
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] --; 
348                                         parser.astPtr --; 
349                                         parser.listLength --;
350                                         parser.currentToken = 0;
351                                 }
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.
356                                 int count;
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;
368                                                 break;
369                                         }
370                                         if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
371                                 }
372                                 if (parser.listLength > 0 && parser.astLengthPtr > 0){
373                                         
374                                         // protection for bugs 15142
375                                         int length = parser.astLengthStack[parser.astLengthPtr];
376                                         int astPtr = parser.astPtr - length;
377                                         boolean canConsume = astPtr >= 0;
378                                         if(canConsume) {
379                                                 if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
380                                                         canConsume = false;
381                                                 }
382                                                 for (int i = 1, max = length + 1; i < max; i++) {
383                                                         if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
384                                                                 canConsume = false;
385                                                         }
386                                                 }
387                                         }
388                                         if(canConsume) {
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;
395                                                 }
396                                         }
397                                 }
398                         }
399                 }
400         }
401 }
402 /*
403  * An opening brace got consumed, might be the expected opening one of the current element,
404  * in which case the bodyStart is updated.
405  */
406 public RecoveredElement updateOnOpeningBrace(int braceEnd){
407
408         /* in case the opening brace is close enough to the signature */
409         if (bracketBalance == 0){
410                 /*
411                         if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd) 
412                                 != parser.scanner.searchLineNumber(braceEnd)){
413                  */
414                 switch(parser().lastIgnoredToken){
415                         case -1 :
416 //                      case TokenNamethrows :
417 //                              break;
418                         default:
419                                 this.foundOpeningBrace = true;                          
420                                 bracketBalance = 1; // pretend the brace was already there
421                 }
422         }       
423         return super.updateOnOpeningBrace(braceEnd);
424 }
425 public void updateParseTree(){
426         this.updatedMethodDeclaration();
427 }
428 /*
429  * Update the declarationSourceEnd of the corresponding parse node
430  */
431 public void updateSourceEndIfNecessary(int sourceEnd){
432         if (this.methodDeclaration.declarationSourceEnd == 0) {
433                 this.methodDeclaration.declarationSourceEnd = sourceEnd;
434                 this.methodDeclaration.bodyEnd = sourceEnd;
435         }
436 }
437 }