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