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.ast;
011:
012: import org.eclipse.jdt.core.compiler.*;
013: import org.eclipse.jdt.internal.compiler.*;
014: import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
015: import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext;
016: import org.eclipse.jdt.internal.compiler.impl.*;
017: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
018: import org.eclipse.jdt.internal.compiler.codegen.*;
019: import org.eclipse.jdt.internal.compiler.lookup.*;
020: import org.eclipse.jdt.internal.compiler.problem.*;
021: import org.eclipse.jdt.internal.compiler.parser.*;
022:
023: public abstract class AbstractMethodDeclaration extends ASTNode
024: implements ProblemSeverities, ReferenceContext {
025:
026: public MethodScope scope;
027: //it is not relevent for constructor but it helps to have the name of the constructor here
028: //which is always the name of the class.....parsing do extra work to fill it up while it do not have to....
029: public char[] selector;
030: public int declarationSourceStart;
031: public int declarationSourceEnd;
032: public int modifiers;
033: public int modifiersSourceStart;
034: public Annotation[] annotations;
035: public Argument[] arguments;
036: public TypeReference[] thrownExceptions;
037: public Statement[] statements;
038: public int explicitDeclarations;
039: public MethodBinding binding;
040: public boolean ignoreFurtherInvestigation = false;
041:
042: public Javadoc javadoc;
043:
044: public int bodyStart;
045: public int bodyEnd = -1;
046: public CompilationResult compilationResult;
047:
048: AbstractMethodDeclaration(CompilationResult compilationResult) {
049: this .compilationResult = compilationResult;
050: }
051:
052: /*
053: * We cause the compilation task to abort to a given extent.
054: */
055: public void abort(int abortLevel, CategorizedProblem problem) {
056:
057: switch (abortLevel) {
058: case AbortCompilation:
059: throw new AbortCompilation(this .compilationResult, problem);
060: case AbortCompilationUnit:
061: throw new AbortCompilationUnit(this .compilationResult,
062: problem);
063: case AbortType:
064: throw new AbortType(this .compilationResult, problem);
065: default:
066: throw new AbortMethod(this .compilationResult, problem);
067: }
068: }
069:
070: public abstract void analyseCode(ClassScope classScope,
071: InitializationFlowContext initializationContext,
072: FlowInfo info);
073:
074: /**
075: * Bind and add argument's binding into the scope of the method
076: */
077: public void bindArguments() {
078:
079: if (this .arguments != null) {
080: // by default arguments in abstract/native methods are considered to be used (no complaint is expected)
081: if (this .binding == null) {
082: for (int i = 0, length = this .arguments.length; i < length; i++) {
083: this .arguments[i].bind(this .scope, null, true);
084: }
085: return;
086: }
087: boolean used = this .binding.isAbstract()
088: || this .binding.isNative();
089: AnnotationBinding[][] paramAnnotations = null;
090: for (int i = 0, length = this .arguments.length; i < length; i++) {
091: Argument argument = this .arguments[i];
092: argument.bind(this .scope, this .binding.parameters[i],
093: used);
094: if (argument.annotations != null) {
095: this .binding.tagBits |= TagBits.HasParameterAnnotations;
096: if (paramAnnotations == null)
097: paramAnnotations = new AnnotationBinding[length][];
098: paramAnnotations[i] = argument.binding
099: .getAnnotations();
100: }
101: }
102: if (paramAnnotations != null)
103: this .binding.setParameterAnnotations(paramAnnotations);
104: }
105: }
106:
107: /**
108: * Record the thrown exception type bindings in the corresponding type references.
109: */
110: public void bindThrownExceptions() {
111:
112: if (this .thrownExceptions != null && this .binding != null
113: && this .binding.thrownExceptions != null) {
114: int thrownExceptionLength = this .thrownExceptions.length;
115: int length = this .binding.thrownExceptions.length;
116: if (length == thrownExceptionLength) {
117: for (int i = 0; i < length; i++) {
118: this .thrownExceptions[i].resolvedType = this .binding.thrownExceptions[i];
119: }
120: } else {
121: int bindingIndex = 0;
122: for (int i = 0; i < thrownExceptionLength
123: && bindingIndex < length; i++) {
124: TypeReference thrownException = this .thrownExceptions[i];
125: ReferenceBinding thrownExceptionBinding = this .binding.thrownExceptions[bindingIndex];
126: char[][] bindingCompoundName = thrownExceptionBinding.compoundName;
127: if (bindingCompoundName == null)
128: continue; // skip problem case
129: if (thrownException instanceof SingleTypeReference) {
130: // single type reference
131: int lengthName = bindingCompoundName.length;
132: char[] thrownExceptionTypeName = thrownException
133: .getTypeName()[0];
134: if (CharOperation.equals(
135: thrownExceptionTypeName,
136: bindingCompoundName[lengthName - 1])) {
137: thrownException.resolvedType = thrownExceptionBinding;
138: bindingIndex++;
139: }
140: } else {
141: // qualified type reference
142: if (CharOperation.equals(thrownException
143: .getTypeName(), bindingCompoundName)) {
144: thrownException.resolvedType = thrownExceptionBinding;
145: bindingIndex++;
146: }
147: }
148: }
149: }
150: }
151: }
152:
153: public CompilationResult compilationResult() {
154:
155: return this .compilationResult;
156: }
157:
158: /**
159: * Bytecode generation for a method
160: * @param classScope
161: * @param classFile
162: */
163: public void generateCode(ClassScope classScope, ClassFile classFile) {
164:
165: int problemResetPC = 0;
166: classFile.codeStream.wideMode = false; // reset wideMode to false
167: if (this .ignoreFurtherInvestigation) {
168: // method is known to have errors, dump a problem method
169: if (this .binding == null)
170: return; // handle methods with invalid signature or duplicates
171: int problemsLength;
172: CategorizedProblem[] problems = this .scope
173: .referenceCompilationUnit().compilationResult
174: .getProblems();
175: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
176: System.arraycopy(problems, 0, problemsCopy, 0,
177: problemsLength);
178: classFile
179: .addProblemMethod(this , this .binding, problemsCopy);
180: return;
181: }
182: // regular code generation
183: try {
184: problemResetPC = classFile.contentsOffset;
185: this .generateCode(classFile);
186: } catch (AbortMethod e) {
187: // a fatal error was detected during code generation, need to restart code gen if possible
188: if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
189: // a branch target required a goto_w, restart code gen in wide mode.
190: try {
191: classFile.contentsOffset = problemResetPC;
192: classFile.methodCount--;
193: classFile.codeStream.wideMode = true; // request wide mode
194: this .generateCode(classFile); // restart method generation
195: } catch (AbortMethod e2) {
196: int problemsLength;
197: CategorizedProblem[] problems = this .scope
198: .referenceCompilationUnit().compilationResult
199: .getAllProblems();
200: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
201: System.arraycopy(problems, 0, problemsCopy, 0,
202: problemsLength);
203: classFile.addProblemMethod(this , this .binding,
204: problemsCopy, problemResetPC);
205: }
206: } else {
207: // produce a problem method accounting for this fatal error
208: int problemsLength;
209: CategorizedProblem[] problems = this .scope
210: .referenceCompilationUnit().compilationResult
211: .getAllProblems();
212: CategorizedProblem[] problemsCopy = new CategorizedProblem[problemsLength = problems.length];
213: System.arraycopy(problems, 0, problemsCopy, 0,
214: problemsLength);
215: classFile.addProblemMethod(this , this .binding,
216: problemsCopy, problemResetPC);
217: }
218: }
219: }
220:
221: public void generateCode(ClassFile classFile) {
222:
223: classFile.generateMethodInfoHeader(this .binding);
224: int methodAttributeOffset = classFile.contentsOffset;
225: int attributeNumber = classFile
226: .generateMethodInfoAttribute(this .binding);
227: if ((!this .binding.isNative()) && (!this .binding.isAbstract())) {
228: int codeAttributeOffset = classFile.contentsOffset;
229: classFile.generateCodeAttributeHeader();
230: CodeStream codeStream = classFile.codeStream;
231: codeStream.reset(this , classFile);
232: // initialize local positions
233: this .scope.computeLocalVariablePositions(this .binding
234: .isStatic() ? 0 : 1, codeStream);
235:
236: // arguments initialization for local variable debug attributes
237: if (this .arguments != null) {
238: for (int i = 0, max = this .arguments.length; i < max; i++) {
239: LocalVariableBinding argBinding;
240: codeStream
241: .addVisibleLocalVariable(argBinding = this .arguments[i].binding);
242: argBinding.recordInitializationStartPC(0);
243: }
244: }
245: if (this .statements != null) {
246: for (int i = 0, max = this .statements.length; i < max; i++)
247: this .statements[i].generateCode(this .scope,
248: codeStream);
249: }
250: // if a problem got reported during code gen, then trigger problem method creation
251: if (this .ignoreFurtherInvestigation) {
252: throw new AbortMethod(this .scope
253: .referenceCompilationUnit().compilationResult,
254: null);
255: }
256: if ((this .bits & ASTNode.NeedFreeReturn) != 0) {
257: codeStream.return_();
258: }
259: // local variable attributes
260: codeStream.exitUserScope(this .scope);
261: codeStream
262: .recordPositionsFrom(0, this .declarationSourceEnd);
263: classFile.completeCodeAttribute(codeAttributeOffset);
264: attributeNumber++;
265: } else {
266: checkArgumentsSize();
267: }
268: classFile.completeMethodInfo(methodAttributeOffset,
269: attributeNumber);
270: }
271:
272: private void checkArgumentsSize() {
273: TypeBinding[] parameters = this .binding.parameters;
274: int size = 1; // an abstact method or a native method cannot be static
275: for (int i = 0, max = parameters.length; i < max; i++) {
276: TypeBinding parameter = parameters[i];
277: if (parameter == TypeBinding.LONG
278: || parameter == TypeBinding.DOUBLE) {
279: size += 2;
280: } else {
281: size++;
282: }
283: if (size > 0xFF) {
284: this .scope.problemReporter()
285: .noMoreAvailableSpaceForArgument(
286: this .scope.locals[i],
287: this .scope.locals[i].declaration);
288: }
289: }
290: }
291:
292: public boolean hasErrors() {
293: return this .ignoreFurtherInvestigation;
294: }
295:
296: public boolean isAbstract() {
297:
298: if (this .binding != null)
299: return this .binding.isAbstract();
300: return (this .modifiers & ClassFileConstants.AccAbstract) != 0;
301: }
302:
303: public boolean isAnnotationMethod() {
304:
305: return false;
306: }
307:
308: public boolean isClinit() {
309:
310: return false;
311: }
312:
313: public boolean isConstructor() {
314:
315: return false;
316: }
317:
318: public boolean isDefaultConstructor() {
319:
320: return false;
321: }
322:
323: public boolean isInitializationMethod() {
324:
325: return false;
326: }
327:
328: public boolean isMethod() {
329:
330: return false;
331: }
332:
333: public boolean isNative() {
334:
335: if (this .binding != null)
336: return this .binding.isNative();
337: return (this .modifiers & ClassFileConstants.AccNative) != 0;
338: }
339:
340: public boolean isStatic() {
341:
342: if (this .binding != null)
343: return this .binding.isStatic();
344: return (this .modifiers & ClassFileConstants.AccStatic) != 0;
345: }
346:
347: /**
348: * Fill up the method body with statement
349: * @param parser
350: * @param unit
351: */
352: public abstract void parseStatements(Parser parser,
353: CompilationUnitDeclaration unit);
354:
355: public StringBuffer print(int tab, StringBuffer output) {
356:
357: if (this .javadoc != null) {
358: this .javadoc.print(tab, output);
359: }
360: printIndent(tab, output);
361: printModifiers(this .modifiers, output);
362: if (this .annotations != null)
363: printAnnotations(this .annotations, output);
364:
365: TypeParameter[] typeParams = typeParameters();
366: if (typeParams != null) {
367: output.append('<');
368: int max = typeParams.length - 1;
369: for (int j = 0; j < max; j++) {
370: typeParams[j].print(0, output);
371: output.append(", ");//$NON-NLS-1$
372: }
373: typeParams[max].print(0, output);
374: output.append('>');
375: }
376:
377: printReturnType(0, output).append(this .selector).append('(');
378: if (this .arguments != null) {
379: for (int i = 0; i < this .arguments.length; i++) {
380: if (i > 0)
381: output.append(", "); //$NON-NLS-1$
382: this .arguments[i].print(0, output);
383: }
384: }
385: output.append(')');
386: if (this .thrownExceptions != null) {
387: output.append(" throws "); //$NON-NLS-1$
388: for (int i = 0; i < this .thrownExceptions.length; i++) {
389: if (i > 0)
390: output.append(", "); //$NON-NLS-1$
391: this .thrownExceptions[i].print(0, output);
392: }
393: }
394: printBody(tab + 1, output);
395: return output;
396: }
397:
398: public StringBuffer printBody(int indent, StringBuffer output) {
399:
400: if (isAbstract()
401: || (this .modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0)
402: return output.append(';');
403:
404: output.append(" {"); //$NON-NLS-1$
405: if (this .statements != null) {
406: for (int i = 0; i < this .statements.length; i++) {
407: output.append('\n');
408: this .statements[i].printStatement(indent, output);
409: }
410: }
411: output.append('\n');
412: printIndent(indent == 0 ? 0 : indent - 1, output).append('}');
413: return output;
414: }
415:
416: public StringBuffer printReturnType(int indent, StringBuffer output) {
417:
418: return output;
419: }
420:
421: public void resolve(ClassScope upperScope) {
422:
423: if (this .binding == null) {
424: this .ignoreFurtherInvestigation = true;
425: }
426:
427: try {
428: bindArguments();
429: bindThrownExceptions();
430: resolveJavadoc();
431: resolveAnnotations(scope, this .annotations, this .binding);
432: resolveStatements();
433: // check @Deprecated annotation presence
434: if (this .binding != null
435: && (this .binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) == 0
436: && (this .binding.modifiers & ClassFileConstants.AccDeprecated) != 0
437: && this .scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
438: this .scope.problemReporter()
439: .missingDeprecatedAnnotationForMethod(this );
440: }
441: } catch (AbortMethod e) {
442: // ========= abort on fatal error =============
443: this .ignoreFurtherInvestigation = true;
444: }
445: }
446:
447: public void resolveJavadoc() {
448:
449: if (this .binding == null)
450: return;
451: if (this .javadoc != null) {
452: this .javadoc.resolve(this .scope);
453: return;
454: }
455: if (this .binding.declaringClass != null
456: && !this .binding.declaringClass.isLocalType()) {
457: this .scope.problemReporter().javadocMissing(
458: this .sourceStart, this .sourceEnd,
459: this .binding.modifiers);
460: }
461: }
462:
463: public void resolveStatements() {
464:
465: if (this .statements != null) {
466: for (int i = 0, length = this .statements.length; i < length; i++) {
467: this .statements[i].resolve(this .scope);
468: }
469: } else if ((this .bits & UndocumentedEmptyBlock) != 0) {
470: this .scope.problemReporter().undocumentedEmptyBlock(
471: this .bodyStart - 1, this .bodyEnd + 1);
472: }
473: }
474:
475: public void tagAsHavingErrors() {
476:
477: this .ignoreFurtherInvestigation = true;
478: }
479:
480: public void traverse(ASTVisitor visitor, ClassScope classScope) {
481: // default implementation: subclass will define it
482: }
483:
484: public TypeParameter[] typeParameters() {
485: return null;
486: }
487: }
|