001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.parser;
011:
012: import org.eclipse.jdt.core.compiler.*;
013: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
014: import org.eclipse.jdt.internal.compiler.ast.Argument;
015: import org.eclipse.jdt.internal.compiler.ast.ASTNode;
016: import org.eclipse.jdt.internal.compiler.ast.Block;
017: import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
018: import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
019: import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
020: import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
021: import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
022: import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
023: import org.eclipse.jdt.internal.compiler.ast.Statement;
024: import org.eclipse.jdt.internal.compiler.ast.SuperReference;
025: import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
026: import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
027: import org.eclipse.jdt.internal.compiler.ast.TypeReference;
028: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
029: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
030: import org.eclipse.jdt.internal.compiler.util.Util;
031:
032: /**
033: * Internal method structure for parsing recovery
034: */
035:
036: public class RecoveredMethod extends RecoveredElement implements
037: TerminalTokens {
038:
039: public AbstractMethodDeclaration methodDeclaration;
040:
041: public RecoveredType[] localTypes;
042: public int localTypeCount;
043:
044: public RecoveredBlock methodBody;
045: public boolean discardBody = true;
046:
047: public RecoveredMethod(AbstractMethodDeclaration methodDeclaration,
048: RecoveredElement parent, int bracketBalance, Parser parser) {
049: super (parent, bracketBalance, parser);
050: this .methodDeclaration = methodDeclaration;
051: this .foundOpeningBrace = !bodyStartsAtHeaderEnd();
052: if (this .foundOpeningBrace) {
053: this .bracketBalance++;
054: }
055: }
056:
057: /*
058: * Record a nested block declaration
059: */
060: public RecoveredElement add(Block nestedBlockDeclaration,
061: int bracketBalanceValue) {
062:
063: /* default behavior is to delegate recording to parent if any,
064: do not consider elements passed the known end (if set)
065: it must be belonging to an enclosing element
066: */
067: if (methodDeclaration.declarationSourceEnd > 0
068: && nestedBlockDeclaration.sourceStart > methodDeclaration.declarationSourceEnd) {
069: if (this .parent == null) {
070: return this ; // ignore
071: } else {
072: return this .parent.add(nestedBlockDeclaration,
073: bracketBalanceValue);
074: }
075: }
076: /* consider that if the opening brace was not found, it is there */
077: if (!foundOpeningBrace) {
078: foundOpeningBrace = true;
079: this .bracketBalance++;
080: }
081:
082: methodBody = new RecoveredBlock(nestedBlockDeclaration, this ,
083: bracketBalanceValue);
084: if (nestedBlockDeclaration.sourceEnd == 0)
085: return methodBody;
086: return this ;
087: }
088:
089: /*
090: * Record a field declaration
091: */
092: public RecoveredElement add(FieldDeclaration fieldDeclaration,
093: int bracketBalanceValue) {
094:
095: /* local variables inside method can only be final and non void */
096: char[][] fieldTypeName;
097: if ((fieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 // local var can only be final
098: || (fieldDeclaration.type == null) // initializer
099: || ((fieldTypeName = fieldDeclaration.type
100: .getTypeName()).length == 1 // non void
101: && CharOperation.equals(fieldTypeName[0],
102: TypeBinding.VOID.sourceName()))) {
103:
104: if (this .parent == null) {
105: return this ; // ignore
106: } else {
107: this
108: .updateSourceEndIfNecessary(this
109: .previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
110: return this .parent.add(fieldDeclaration,
111: bracketBalanceValue);
112: }
113: }
114: /* default behavior is to delegate recording to parent if any,
115: do not consider elements passed the known end (if set)
116: it must be belonging to an enclosing element
117: */
118: if (methodDeclaration.declarationSourceEnd > 0
119: && fieldDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd) {
120: if (this .parent == null) {
121: return this ; // ignore
122: } else {
123: return this .parent.add(fieldDeclaration,
124: bracketBalanceValue);
125: }
126: }
127: /* consider that if the opening brace was not found, it is there */
128: if (!foundOpeningBrace) {
129: foundOpeningBrace = true;
130: this .bracketBalance++;
131: }
132: // still inside method, treat as local variable
133: return this ; // ignore
134: }
135:
136: /*
137: * Record a local declaration - regular method should have been created a block body
138: */
139: public RecoveredElement add(LocalDeclaration localDeclaration,
140: int bracketBalanceValue) {
141:
142: /* local variables inside method can only be final and non void */
143: /*
144: char[][] localTypeName;
145: if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
146: || (localDeclaration.type == null) // initializer
147: || ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
148: && CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){
149:
150: if (this.parent == null){
151: return this; // ignore
152: } else {
153: this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
154: return this.parent.add(localDeclaration, bracketBalance);
155: }
156: }
157: */
158: /* do not consider a type starting passed the type end (if set)
159: it must be belonging to an enclosing type */
160: if (methodDeclaration.declarationSourceEnd != 0
161: && localDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd) {
162:
163: if (this .parent == null) {
164: return this ; // ignore
165: } else {
166: return this .parent.add(localDeclaration,
167: bracketBalanceValue);
168: }
169: }
170: if (methodBody == null) {
171: Block block = new Block(0);
172: block.sourceStart = methodDeclaration.bodyStart;
173: RecoveredElement currentBlock = this .add(block, 1);
174: if (this .bracketBalance > 0) {
175: for (int i = 0; i < this .bracketBalance - 1; i++) {
176: currentBlock = currentBlock.add(new Block(0), 1);
177: }
178: this .bracketBalance = 1;
179: }
180: return currentBlock.add(localDeclaration,
181: bracketBalanceValue);
182: }
183: return methodBody.add(localDeclaration, bracketBalanceValue,
184: true);
185: }
186:
187: /*
188: * Record a statement - regular method should have been created a block body
189: */
190: public RecoveredElement add(Statement statement,
191: int bracketBalanceValue) {
192:
193: /* do not consider a type starting passed the type end (if set)
194: it must be belonging to an enclosing type */
195: if (methodDeclaration.declarationSourceEnd != 0
196: && statement.sourceStart > methodDeclaration.declarationSourceEnd) {
197:
198: if (this .parent == null) {
199: return this ; // ignore
200: } else {
201: return this .parent.add(statement, bracketBalanceValue);
202: }
203: }
204: if (methodBody == null) {
205: Block block = new Block(0);
206: block.sourceStart = methodDeclaration.bodyStart;
207: RecoveredElement currentBlock = this .add(block, 1);
208: if (this .bracketBalance > 0) {
209: for (int i = 0; i < this .bracketBalance - 1; i++) {
210: currentBlock = currentBlock.add(new Block(0), 1);
211: }
212: this .bracketBalance = 1;
213: }
214: return currentBlock.add(statement, bracketBalanceValue);
215: }
216: return methodBody.add(statement, bracketBalanceValue, true);
217: }
218:
219: public RecoveredElement add(TypeDeclaration typeDeclaration,
220: int bracketBalanceValue) {
221:
222: /* do not consider a type starting passed the type end (if set)
223: it must be belonging to an enclosing type */
224: if (methodDeclaration.declarationSourceEnd != 0
225: && typeDeclaration.declarationSourceStart > methodDeclaration.declarationSourceEnd) {
226:
227: if (this .parent == null) {
228: return this ; // ignore
229: }
230: return this .parent
231: .add(typeDeclaration, bracketBalanceValue);
232: }
233: if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0
234: || this .parser().methodRecoveryActivated
235: || this .parser().statementRecoveryActivated) {
236: if (methodBody == null) {
237: Block block = new Block(0);
238: block.sourceStart = methodDeclaration.bodyStart;
239: this .add(block, 1);
240: }
241: return methodBody.add(typeDeclaration, bracketBalanceValue,
242: true);
243: }
244: switch (TypeDeclaration.kind(typeDeclaration.modifiers)) {
245: case TypeDeclaration.INTERFACE_DECL:
246: case TypeDeclaration.ANNOTATION_TYPE_DECL:
247: this
248: .updateSourceEndIfNecessary(this
249: .previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
250: if (this .parent == null) {
251: return this ; // ignore
252: }
253: // close the constructor
254: return this .parent
255: .add(typeDeclaration, bracketBalanceValue);
256: }
257: if (localTypes == null) {
258: localTypes = new RecoveredType[5];
259: localTypeCount = 0;
260: } else {
261: if (localTypeCount == localTypes.length) {
262: System
263: .arraycopy(
264: localTypes,
265: 0,
266: (localTypes = new RecoveredType[2 * localTypeCount]),
267: 0, localTypeCount);
268: }
269: }
270: RecoveredType element = new RecoveredType(typeDeclaration,
271: this , bracketBalanceValue);
272: localTypes[localTypeCount++] = element;
273:
274: /* consider that if the opening brace was not found, it is there */
275: if (!foundOpeningBrace) {
276: foundOpeningBrace = true;
277: this .bracketBalance++;
278: }
279: return element;
280: }
281:
282: public boolean bodyStartsAtHeaderEnd() {
283: return methodDeclaration.bodyStart == methodDeclaration.sourceEnd + 1;
284: }
285:
286: /*
287: * Answer the associated parsed structure
288: */
289: public ASTNode parseTree() {
290: return methodDeclaration;
291: }
292:
293: /*
294: * Answer the very source end of the corresponding parse node
295: */
296: public int sourceEnd() {
297: return this .methodDeclaration.declarationSourceEnd;
298: }
299:
300: public String toString(int tab) {
301: StringBuffer result = new StringBuffer(tabString(tab));
302: result.append("Recovered method:\n"); //$NON-NLS-1$
303: this .methodDeclaration.print(tab + 1, result);
304: if (this .localTypes != null) {
305: for (int i = 0; i < this .localTypeCount; i++) {
306: result.append("\n"); //$NON-NLS-1$
307: result.append(this .localTypes[i].toString(tab + 1));
308: }
309: }
310: if (this .methodBody != null) {
311: result.append("\n"); //$NON-NLS-1$
312: result.append(this .methodBody.toString(tab + 1));
313: }
314: return result.toString();
315: }
316:
317: /*
318: * Update the bodyStart of the corresponding parse node
319: */
320: public void updateBodyStart(int bodyStart) {
321: this .foundOpeningBrace = true;
322: this .methodDeclaration.bodyStart = bodyStart;
323: }
324:
325: public AbstractMethodDeclaration updatedMethodDeclaration() {
326:
327: if (methodBody != null) {
328: Block block = methodBody.updatedBlock();
329: if (block != null) {
330: methodDeclaration.statements = block.statements;
331:
332: /* first statement might be an explict constructor call destinated to a special slot */
333: if (methodDeclaration.isConstructor()) {
334: ConstructorDeclaration constructor = (ConstructorDeclaration) methodDeclaration;
335: if (methodDeclaration.statements != null
336: && methodDeclaration.statements[0] instanceof ExplicitConstructorCall) {
337: constructor.constructorCall = (ExplicitConstructorCall) methodDeclaration.statements[0];
338: int length = methodDeclaration.statements.length;
339: System
340: .arraycopy(
341: methodDeclaration.statements,
342: 1,
343: (methodDeclaration.statements = new Statement[length - 1]),
344: 0, length - 1);
345: }
346: if (constructor.constructorCall == null) { // add implicit constructor call
347: constructor.constructorCall = SuperReference
348: .implicitSuperConstructorCall();
349: }
350: }
351: }
352: }
353: if (localTypeCount > 0)
354: methodDeclaration.bits |= ASTNode.HasLocalType;
355: return methodDeclaration;
356: }
357:
358: /*
359: * Update the corresponding parse node from parser state which
360: * is about to disappear because of restarting recovery
361: */
362: public void updateFromParserState() {
363: // if parent is null then recovery already occured in diet parser.
364: if (this .bodyStartsAtHeaderEnd() && this .parent != null) {
365: Parser parser = this .parser();
366: /* might want to recover arguments or thrown exceptions */
367: if (parser.listLength > 0 && parser.astLengthPtr > 0) { // awaiting interface type references
368: /* has consumed the arguments - listed elements must be thrown exceptions */
369: if (methodDeclaration.sourceEnd == parser.rParenPos) {
370:
371: // protection for bugs 15142
372: int length = parser.astLengthStack[parser.astLengthPtr];
373: int astPtr = parser.astPtr - length;
374: boolean canConsume = astPtr >= 0;
375: if (canConsume) {
376: if ((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
377: canConsume = false;
378: }
379: for (int i = 1, max = length + 1; i < max; i++) {
380: if (!(parser.astStack[astPtr + i] instanceof TypeReference)) {
381: canConsume = false;
382: }
383: }
384: }
385: if (canConsume) {
386: parser.consumeMethodHeaderThrowsClause();
387: // will reset typeListLength to zero
388: // thus this check will only be performed on first errorCheck after void foo() throws X, Y,
389: } else {
390: parser.listLength = 0;
391: }
392: } else {
393: /* has not consumed arguments yet, listed elements must be arguments */
394: if (parser.currentToken == TokenNameLPAREN
395: || parser.currentToken == TokenNameSEMICOLON) {
396: /* if currentToken is parenthesis this last argument is a method/field signature */
397: parser.astLengthStack[parser.astLengthPtr]--;
398: parser.astPtr--;
399: parser.listLength--;
400: parser.currentToken = 0;
401: }
402: int argLength = parser.astLengthStack[parser.astLengthPtr];
403: int argStart = parser.astPtr - argLength + 1;
404: boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used
405:
406: // remove unfinished annotation nodes
407: MemberValuePair[] memberValuePairs = null;
408: if (argLength > 0
409: && parser.astStack[parser.astPtr] instanceof MemberValuePair) {
410: System
411: .arraycopy(
412: parser.astStack,
413: argStart,
414: memberValuePairs = new MemberValuePair[argLength],
415: 0, argLength);
416: parser.astLengthPtr--;
417: parser.astPtr -= argLength;
418:
419: argLength = parser.astLengthStack[parser.astLengthPtr];
420: argStart = parser.astPtr - argLength + 1;
421: needUpdateRParenPos = true;
422: }
423:
424: // to compute bodyStart, and thus used to set next checkpoint.
425: int count;
426: for (count = 0; count < argLength; count++) {
427: ASTNode aNode = parser.astStack[argStart
428: + count];
429: if (aNode instanceof Argument) {
430: Argument argument = (Argument) aNode;
431: /* cannot be an argument if non final */
432: char[][] argTypeName = argument.type
433: .getTypeName();
434: if ((argument.modifiers & ~ClassFileConstants.AccFinal) != 0
435: || (argTypeName.length == 1 && CharOperation
436: .equals(
437: argTypeName[0],
438: TypeBinding.VOID
439: .sourceName()))) {
440: parser.astLengthStack[parser.astLengthPtr] = count;
441: parser.astPtr = argStart + count - 1;
442: parser.listLength = count;
443: parser.currentToken = 0;
444: break;
445: }
446: if (needUpdateRParenPos)
447: parser.rParenPos = argument.sourceEnd + 1;
448: } else {
449: parser.astLengthStack[parser.astLengthPtr] = count;
450: parser.astPtr = argStart + count - 1;
451: parser.listLength = count;
452: parser.currentToken = 0;
453: break;
454: }
455: }
456: if (parser.listLength > 0
457: && parser.astLengthPtr > 0) {
458:
459: // protection for bugs 15142
460: int length = parser.astLengthStack[parser.astLengthPtr];
461: int astPtr = parser.astPtr - length;
462: boolean canConsume = astPtr >= 0;
463: if (canConsume) {
464: if ((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
465: canConsume = false;
466: }
467: for (int i = 1, max = length + 1; i < max; i++) {
468: if (!(parser.astStack[astPtr + i] instanceof Argument)) {
469: canConsume = false;
470: }
471: }
472: }
473: if (canConsume) {
474: parser.consumeMethodHeaderRightParen();
475: /* fix-up positions, given they were updated against rParenPos, which did not get set */
476: if (parser.currentElement == this ) { // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
477: methodDeclaration.sourceEnd = methodDeclaration.arguments[methodDeclaration.arguments.length - 1].sourceEnd;
478: methodDeclaration.bodyStart = methodDeclaration.sourceEnd + 1;
479: parser.lastCheckPoint = methodDeclaration.bodyStart;
480: }
481: }
482: }
483:
484: if (memberValuePairs != null) {
485: System.arraycopy(memberValuePairs, 0,
486: parser.astStack, parser.astPtr + 1,
487: memberValuePairs.length);
488: parser.astPtr += memberValuePairs.length;
489: parser.astLengthStack[++parser.astLengthPtr] = memberValuePairs.length;
490: }
491: }
492: }
493: }
494: }
495:
496: public RecoveredElement updateOnClosingBrace(int braceStart,
497: int braceEnd) {
498: if (this .methodDeclaration.isAnnotationMethod()) {
499: this .updateSourceEndIfNecessary(braceStart, braceEnd);
500: if (!this .foundOpeningBrace && this .parent != null) {
501: return this .parent.updateOnClosingBrace(braceStart,
502: braceEnd);
503: }
504: return this ;
505: }
506: if (this .parent != null && this .parent instanceof RecoveredType) {
507: int modifiers = ((RecoveredType) this .parent).typeDeclaration.modifiers;
508: if (TypeDeclaration.kind(modifiers) == TypeDeclaration.INTERFACE_DECL) {
509: if (!this .foundOpeningBrace) {
510: this .updateSourceEndIfNecessary(braceStart - 1,
511: braceStart - 1);
512: return this .parent.updateOnClosingBrace(braceStart,
513: braceEnd);
514: }
515: }
516: }
517: return super .updateOnClosingBrace(braceStart, braceEnd);
518: }
519:
520: /*
521: * An opening brace got consumed, might be the expected opening one of the current element,
522: * in which case the bodyStart is updated.
523: */
524: public RecoveredElement updateOnOpeningBrace(int braceStart,
525: int braceEnd) {
526:
527: /* in case the opening brace is close enough to the signature */
528: if (bracketBalance == 0) {
529: /*
530: if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
531: != parser.scanner.searchLineNumber(braceEnd)){
532: */
533: switch (parser().lastIgnoredToken) {
534: case -1:
535: case TokenNamethrows:
536: break;
537: default:
538: this .foundOpeningBrace = true;
539: bracketBalance = 1; // pretend the brace was already there
540: }
541: }
542: return super .updateOnOpeningBrace(braceStart, braceEnd);
543: }
544:
545: public void updateParseTree() {
546: this .updatedMethodDeclaration();
547: }
548:
549: /*
550: * Update the declarationSourceEnd of the corresponding parse node
551: */
552: public void updateSourceEndIfNecessary(int braceStart, int braceEnd) {
553: if (this .methodDeclaration.declarationSourceEnd == 0) {
554: if (parser().rBraceSuccessorStart >= braceEnd) {
555: this .methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
556: this .methodDeclaration.bodyEnd = parser().rBraceStart;
557: } else {
558: this .methodDeclaration.declarationSourceEnd = braceEnd;
559: this .methodDeclaration.bodyEnd = braceStart - 1;
560: }
561: }
562: }
563:
564: void attach(TypeParameter[] parameters, int startPos) {
565: if (methodDeclaration.modifiers != ClassFileConstants.AccDefault)
566: return;
567:
568: int lastParameterEnd = parameters[parameters.length - 1].sourceEnd;
569:
570: Parser parser = this .parser();
571: Scanner scanner = parser.scanner;
572: if (Util.getLineNumber(
573: methodDeclaration.declarationSourceStart,
574: scanner.lineEnds, 0, scanner.linePtr) != Util
575: .getLineNumber(lastParameterEnd, scanner.lineEnds, 0,
576: scanner.linePtr))
577: return;
578:
579: if (parser.modifiersSourceStart > lastParameterEnd
580: && parser.modifiersSourceStart < methodDeclaration.declarationSourceStart)
581: return;
582:
583: if (this .methodDeclaration instanceof MethodDeclaration) {
584: ((MethodDeclaration) this .methodDeclaration).typeParameters = parameters;
585: this .methodDeclaration.declarationSourceStart = startPos;
586: } else if (this .methodDeclaration instanceof ConstructorDeclaration) {
587: ((ConstructorDeclaration) this.methodDeclaration).typeParameters = parameters;
588: this.methodDeclaration.declarationSourceStart = startPos;
589: }
590: }
591: }
|