214dc4c5492cfefc66b5ac958b4a606b9b8ba66f
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / AbstractMethodDeclaration.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.ast;
12
13 import net.sourceforge.phpdt.core.compiler.*;
14 import net.sourceforge.phpdt.internal.compiler.*;
15 import net.sourceforge.phpdt.internal.compiler.impl.*;
16 import net.sourceforge.phpdt.internal.compiler.codegen.*;
17 import net.sourceforge.phpdt.internal.compiler.flow.*;
18 import net.sourceforge.phpdt.internal.compiler.lookup.*;
19 import net.sourceforge.phpdt.internal.compiler.problem.*;
20 import net.sourceforge.phpdt.internal.compiler.parser.*;
21
22 public abstract class AbstractMethodDeclaration
23         extends AstNode
24         implements ProblemSeverities, ReferenceContext {
25                 
26         public MethodScope scope;
27         //it is not relevent for constructor but it helps to have the name of the constructor here 
28         //which is always the name of the class.....parsing do extra work to fill it up while it do not have to....
29         public char[] selector;
30         public int declarationSourceStart;
31         public int declarationSourceEnd;
32         public int modifiers;
33         public int modifiersSourceStart;
34         public Argument[] arguments;
35         public TypeReference[] thrownExceptions;
36         public Statement[] statements;
37         public int explicitDeclarations;
38         public MethodBinding binding;
39         public boolean ignoreFurtherInvestigation = false;
40         public boolean needFreeReturn = false;
41
42         public int bodyStart;
43         public int bodyEnd = -1;
44         public CompilationResult compilationResult;
45         
46         AbstractMethodDeclaration(CompilationResult compilationResult){
47                 this.compilationResult = compilationResult;
48         }
49         
50         /*
51          *      We cause the compilation task to abort to a given extent.
52          */
53         public void abort(int abortLevel) {
54
55                 if (scope == null) {
56                         throw new AbortCompilation(); // cannot do better
57                 }
58
59                 CompilationResult compilationResult =
60                         scope.referenceCompilationUnit().compilationResult;
61
62                 switch (abortLevel) {
63                         case AbortCompilation :
64                                 throw new AbortCompilation(compilationResult);
65                         case AbortCompilationUnit :
66                                 throw new AbortCompilationUnit(compilationResult);
67                         case AbortType :
68                                 throw new AbortType(compilationResult);
69                         default :
70                                 throw new AbortMethod(compilationResult);
71                 }
72         }
73
74         public void analyseCode(
75                 ClassScope currentScope,
76                 FlowContext flowContext,
77                 FlowInfo flowInfo) {
78
79                 // starting of the code analysis for methods
80                 if (ignoreFurtherInvestigation)
81                         return;
82                 try {
83                         if (binding == null)
84                                 return;
85                         // may be in a non necessary <clinit> for innerclass with static final constant fields
86                         if (binding.isAbstract() || binding.isNative())
87                                 return;
88
89                         ExceptionHandlingFlowContext methodContext =
90                                 new ExceptionHandlingFlowContext(
91                                         flowContext,
92                                         this,
93                                         binding.thrownExceptions,
94                                         scope,
95                                         FlowInfo.DeadEnd);
96
97                         // propagate to statements
98                         if (statements != null) {
99                                 for (int i = 0, count = statements.length; i < count; i++) {
100                                         Statement stat;
101                                         if (!flowInfo.complainIfUnreachable((stat = statements[i]), scope)) {
102                                                 flowInfo = stat.analyseCode(scope, methodContext, flowInfo);
103                                         }
104                                 }
105                         }
106                         // check for missing returning path
107                         TypeBinding returnType = binding.returnType;
108                         if ((returnType == VoidBinding) || isAbstract()) {
109                                 needFreeReturn =
110                                         !((flowInfo == FlowInfo.DeadEnd) || flowInfo.isFakeReachable());
111                         } else {
112                                 if (flowInfo != FlowInfo.DeadEnd) {
113                                         // special test for empty methods that should return something
114                                         if ((statements == null) && (returnType != VoidBinding)) {
115                                                 scope.problemReporter().shouldReturn(returnType, this);
116                                         } else {
117                                                 scope.problemReporter().shouldReturn(
118                                                         returnType,
119                                                         statements[statements.length - 1]);
120                                         }
121                                 }
122                         }
123                 } catch (AbortMethod e) {
124                         this.ignoreFurtherInvestigation = true;
125                 }
126         }
127
128         /**
129          * Bind and add argument's binding into the scope of the method
130          */
131         public void bindArguments() {
132
133                 if (arguments != null) {
134                         // by default arguments in abstract/native methods are considered to be used (no complaint is expected)
135                         boolean used = binding == null || binding.isAbstract() || binding.isNative();
136
137                         int length = arguments.length;
138                         for (int i = 0; i < length; i++) {
139                                 TypeBinding argType = binding == null ? null : binding.parameters[i];
140                                 arguments[i].bind(scope, argType, used);
141                         }
142                 }
143         }
144
145         /**
146          * Record the thrown exception type bindings in the corresponding type references.
147          */
148         public void bindThrownExceptions() {
149
150                 if (this.thrownExceptions != null
151                         && this.binding != null
152                         && this.binding.thrownExceptions != null) {
153                         int length = this.binding.thrownExceptions.length;
154                         for (int i = 0; i < length; i++) {
155                                 this.thrownExceptions[i].binding = this.binding.thrownExceptions[i];
156                         }
157                 }
158         }
159
160         public CompilationResult compilationResult() {
161                 
162                 return this.compilationResult;
163         }
164         
165         /**
166          * Bytecode generation for a method
167          */
168         public void generateCode(ClassScope classScope, ClassFile classFile) {
169                 
170                 int problemResetPC = 0;
171                 classFile.codeStream.wideMode = false; // reset wideMode to false
172                 if (ignoreFurtherInvestigation) {
173                         // method is known to have errors, dump a problem method
174                         if (this.binding == null)
175                                 return; // handle methods with invalid signature or duplicates
176                         int problemsLength;
177                         IProblem[] problems =
178                                 scope.referenceCompilationUnit().compilationResult.getProblems();
179                         IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
180                         System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
181                         classFile.addProblemMethod(this, binding, problemsCopy);
182                         return;
183                 }
184                 // regular code generation
185                 try {
186                         problemResetPC = classFile.contentsOffset;
187                         this.generateCode(classFile);
188                 } catch (AbortMethod e) {
189                         // a fatal error was detected during code generation, need to restart code gen if possible
190                         if (e.compilationResult == CodeStream.RESTART_IN_WIDE_MODE) {
191                                 // a branch target required a goto_w, restart code gen in wide mode.
192                                 try {
193                                         this.traverse(new ResetStateForCodeGenerationVisitor(), classScope);
194                                         classFile.contentsOffset = problemResetPC;
195                                         classFile.methodCount--;
196                                         classFile.codeStream.wideMode = true; // request wide mode 
197                                         this.generateCode(classFile); // restart method generation
198                                 } catch (AbortMethod e2) {
199                                         int problemsLength;
200                                         IProblem[] problems =
201                                                 scope.referenceCompilationUnit().compilationResult.getProblems();
202                                         IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
203                                         System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
204                                         classFile.addProblemMethod(this, binding, problemsCopy, problemResetPC);
205                                 }
206                         } else {
207                                 // produce a problem method accounting for this fatal error
208                                 int problemsLength;
209                                 IProblem[] problems =
210                                         scope.referenceCompilationUnit().compilationResult.getProblems();
211                                 IProblem[] problemsCopy = new IProblem[problemsLength = problems.length];
212                                 System.arraycopy(problems, 0, problemsCopy, 0, problemsLength);
213                                 classFile.addProblemMethod(this, binding, problemsCopy, problemResetPC);
214                         }
215                 }
216         }
217
218         private void generateCode(ClassFile classFile) {
219
220                 classFile.generateMethodInfoHeader(binding);
221                 int methodAttributeOffset = classFile.contentsOffset;
222                 int attributeNumber = classFile.generateMethodInfoAttribute(binding);
223                 if ((!binding.isNative()) && (!binding.isAbstract())) {
224                         int codeAttributeOffset = classFile.contentsOffset;
225                         classFile.generateCodeAttributeHeader();
226                         CodeStream codeStream = classFile.codeStream;
227                         codeStream.reset(this, classFile);
228                         // initialize local positions
229                         scope.computeLocalVariablePositions(binding.isStatic() ? 0 : 1, codeStream);
230
231                         // arguments initialization for local variable debug attributes
232                         if (arguments != null) {
233                                 for (int i = 0, max = arguments.length; i < max; i++) {
234                                         LocalVariableBinding argBinding;
235                                         codeStream.addVisibleLocalVariable(argBinding = arguments[i].binding);
236                                         argBinding.recordInitializationStartPC(0);
237                                 }
238                         }
239                         if (statements != null) {
240                                 for (int i = 0, max = statements.length; i < max; i++)
241                                         statements[i].generateCode(scope, codeStream);
242                         }
243                         if (needFreeReturn) {
244                                 codeStream.return_();
245                         }
246                         // local variable attributes
247                         codeStream.exitUserScope(scope);
248                         codeStream.recordPositionsFrom(0, this.bodyEnd);
249                         classFile.completeCodeAttribute(codeAttributeOffset);
250                         attributeNumber++;
251                 }
252                 classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
253
254                 // if a problem got reported during code gen, then trigger problem method creation
255                 if (ignoreFurtherInvestigation) {
256                         throw new AbortMethod(scope.referenceCompilationUnit().compilationResult);
257                 }
258         }
259
260         public boolean hasErrors() {
261                 return this.ignoreFurtherInvestigation;
262         }
263
264         public boolean isAbstract() {
265
266                 if (binding != null)
267                         return binding.isAbstract();
268                 return (modifiers & AccAbstract) != 0;
269         }
270
271         public boolean isClinit() {
272
273                 return false;
274         }
275
276         public boolean isConstructor() {
277
278                 return false;
279         }
280
281         public boolean isDefaultConstructor() {
282
283                 return false;
284         }
285
286         public boolean isInitializationMethod() {
287
288                 return false;
289         }
290
291         public boolean isNative() {
292
293                 if (binding != null)
294                         return binding.isNative();
295                 return (modifiers & AccNative) != 0;
296         }
297
298         public boolean isStatic() {
299
300                 if (binding != null)
301                         return binding.isStatic();
302                 return (modifiers & AccStatic) != 0;
303         }
304
305         /**
306          * Fill up the method body with statement
307          */
308         public abstract void parseStatements(
309                 Parser parser,
310                 CompilationUnitDeclaration unit);
311
312         public void resolve(ClassScope upperScope) {
313
314                 if (binding == null) {
315                         ignoreFurtherInvestigation = true;
316                 }
317
318                 try {
319                         bindArguments(); 
320                         bindThrownExceptions();
321                         resolveStatements(upperScope);
322                 } catch (AbortMethod e) {       // ========= abort on fatal error =============
323                         this.ignoreFurtherInvestigation = true;
324                 } 
325         }
326
327         public void resolveStatements(ClassScope upperScope) {
328
329                 if (statements != null) {
330                         int i = 0, length = statements.length;
331                         while (i < length)
332                                 statements[i++].resolve(scope);
333                 }
334         }
335
336         public String returnTypeToString(int tab) {
337
338                 return ""; //$NON-NLS-1$
339         }
340
341         public void tagAsHavingErrors() {
342
343                 ignoreFurtherInvestigation = true;
344         }
345
346         public String toString(int tab) {
347
348                 String s = tabString(tab);
349                 if (modifiers != AccDefault) {
350                         s += modifiersString(modifiers);
351                 }
352
353                 s += returnTypeToString(0);
354                 s += new String(selector) + "("; //$NON-NLS-1$
355                 if (arguments != null) {
356                         for (int i = 0; i < arguments.length; i++) {
357                                 s += arguments[i].toString(0);
358                                 if (i != (arguments.length - 1))
359                                         s = s + ", "; //$NON-NLS-1$
360                         };
361                 };
362                 s += ")"; //$NON-NLS-1$
363                 if (thrownExceptions != null) {
364                         s += " throws "; //$NON-NLS-1$
365                         for (int i = 0; i < thrownExceptions.length; i++) {
366                                 s += thrownExceptions[i].toString(0);
367                                 if (i != (thrownExceptions.length - 1))
368                                         s = s + ", "; //$NON-NLS-1$
369                         };
370                 };
371
372                 s += toStringStatements(tab + 1);
373                 return s;
374         }
375
376         public String toStringStatements(int tab) {
377
378                 if (isAbstract() || (this.modifiers & AccSemicolonBody) != 0)
379                         return ";"; //$NON-NLS-1$
380
381                 String s = " {"; //$NON-NLS-1$
382                 if (statements != null) {
383                         for (int i = 0; i < statements.length; i++) {
384                                 s = s + "\n" + statements[i].toString(tab); //$NON-NLS-1$
385                                 if (!(statements[i] instanceof Block)) {
386                                         s += ";"; //$NON-NLS-1$
387                                 }
388                         }
389                 }
390                 s += "\n" + tabString(tab == 0 ? 0 : tab - 1) + "}"; //$NON-NLS-2$ //$NON-NLS-1$
391                 return s;
392         }
393
394         public void traverse(
395                 IAbstractSyntaxTreeVisitor visitor,
396                 ClassScope classScope) {
397         }
398 }