Basic Reafctoring functionality adapted from Leif Frenzels sources in eclipse-magazin...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / MethodScope.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
12
13 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
14 import net.sourceforge.phpdt.internal.compiler.ast.ConstructorDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.QualifiedNameReference;
16 import net.sourceforge.phpdt.internal.compiler.ast.SingleNameReference;
17 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
18 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
19 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
20 import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
21 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
22
23 /**
24  * Particular block scope used for methods, constructors or clinits, representing
25  * its outermost blockscope. Note also that such a scope will be provided to enclose
26  * field initializers subscopes as well.
27  */
28 public class MethodScope extends BlockScope {
29
30         public ReferenceContext referenceContext;
31         public boolean isStatic; // method modifier or initializer one
32
33         //fields used during name resolution
34         public static final int NotInFieldDecl = -1; //must be a negative value 
35         public boolean isConstructorCall = false; 
36         public FieldBinding initializedField; // the field being initialized
37         public int fieldDeclarationIndex = NotInFieldDecl; 
38
39         // flow analysis
40         public int analysisIndex; // for setting flow-analysis id
41         public boolean isPropagatingInnerClassEmulation;
42
43         // for local variables table attributes
44         public int lastIndex = 0;
45         public long[] definiteInits = new long[4];
46         public long[][] extraDefiniteInits = new long[4][];
47
48         // inner-emulation
49         public SyntheticArgumentBinding[] extraSyntheticArguments;
50         
51         public MethodScope(ClassScope parent, ReferenceContext context, boolean isStatic) {
52
53                 super(METHOD_SCOPE, parent);
54                 locals = new LocalVariableBinding[5];
55                 this.referenceContext = context;
56                 this.isStatic = isStatic;
57                 this.startIndex = 0;
58         }
59
60         /* Spec : 8.4.3 & 9.4
61          */
62         private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
63                 
64                 int modifiers = methodBinding.modifiers;
65                 if ((modifiers & AccAlternateModifierProblem) != 0)
66                         problemReporter().duplicateModifierForMethod(
67                                 methodBinding.declaringClass,
68                                 (AbstractMethodDeclaration) referenceContext);
69
70                 if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
71                         if (methodBinding.declaringClass.isPublic())
72                                 modifiers |= AccPublic;
73                         else if (methodBinding.declaringClass.isProtected())
74                                 modifiers |= AccProtected;
75                 }
76
77                 // after this point, tests on the 16 bits reserved.
78                 int realModifiers = modifiers & AccJustFlag;
79
80                 // check for abnormal modifiers
81                 int unexpectedModifiers =
82                         ~(AccPublic | AccPrivate | AccProtected);// | AccStrictfp);
83                 if ((realModifiers & unexpectedModifiers) != 0)
84                         problemReporter().illegalModifierForMethod(
85                                 methodBinding.declaringClass,
86                                 (AbstractMethodDeclaration) referenceContext);
87 //              else if (
88 //                      (((AbstractMethodDeclaration) referenceContext).modifiers & AccStrictfp) != 0)
89 //                      // must check the parse node explicitly
90 //                      problemReporter().illegalModifierForMethod(
91 //                              methodBinding.declaringClass,
92 //                              (AbstractMethodDeclaration) referenceContext);
93
94                 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
95                 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
96                 if ((accessorBits & (accessorBits - 1)) != 0) {
97                         problemReporter().illegalVisibilityModifierCombinationForMethod(
98                                 methodBinding.declaringClass,
99                                 (AbstractMethodDeclaration) referenceContext);
100
101                         // need to keep the less restrictive
102                         if ((accessorBits & AccPublic) != 0) {
103                                 if ((accessorBits & AccProtected) != 0)
104                                         modifiers ^= AccProtected;
105                                 if ((accessorBits & AccPrivate) != 0)
106                                         modifiers ^= AccPrivate;
107                         }
108                         if ((accessorBits & AccProtected) != 0)
109                                 if ((accessorBits & AccPrivate) != 0)
110                                         modifiers ^= AccPrivate;
111                 }
112
113                 // if the receiver's declaring class is a private nested type, then make sure the receiver is not private (causes problems for inner type emulation)
114                 if (methodBinding.declaringClass.isPrivate())
115                         if ((modifiers & AccPrivate) != 0)
116                                 modifiers ^= AccPrivate;
117
118                 methodBinding.modifiers = modifiers;
119         }
120         
121         /* Spec : 8.4.3 & 9.4
122          */
123         private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
124                 
125                 int modifiers = methodBinding.modifiers;
126                 if ((modifiers & AccAlternateModifierProblem) != 0)
127                         problemReporter().duplicateModifierForMethod(
128                                 methodBinding.declaringClass,
129                                 (AbstractMethodDeclaration) referenceContext);
130
131                 // after this point, tests on the 16 bits reserved.
132                 int realModifiers = modifiers & AccJustFlag;
133
134                 // set the requested modifiers for a method in an interface
135                 if (methodBinding.declaringClass.isInterface()) {
136                         if ((realModifiers & ~(AccPublic | AccAbstract)) != 0)
137                                 problemReporter().illegalModifierForInterfaceMethod(
138                                         methodBinding.declaringClass,
139                                         (AbstractMethodDeclaration) referenceContext);
140                         return;
141                 }
142
143                 // check for abnormal modifiers
144                 int unexpectedModifiers =
145                         ~(
146                                 AccPublic
147                                         | AccPrivate
148                                         | AccProtected
149                                         | AccAbstract
150                                         | AccStatic
151                                         | AccFinal);
152 //                                      | AccSynchronized
153 //                                      | AccNative
154 //                                      | AccStrictfp);
155                 if ((realModifiers & unexpectedModifiers) != 0)
156                         problemReporter().illegalModifierForMethod(
157                                 methodBinding.declaringClass,
158                                 (AbstractMethodDeclaration) referenceContext);
159
160                 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
161                 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
162                 if ((accessorBits & (accessorBits - 1)) != 0) {
163                         problemReporter().illegalVisibilityModifierCombinationForMethod(
164                                 methodBinding.declaringClass,
165                                 (AbstractMethodDeclaration) referenceContext);
166
167                         // need to keep the less restrictive
168                         if ((accessorBits & AccPublic) != 0) {
169                                 if ((accessorBits & AccProtected) != 0)
170                                         modifiers ^= AccProtected;
171                                 if ((accessorBits & AccPrivate) != 0)
172                                         modifiers ^= AccPrivate;
173                         }
174                         if ((accessorBits & AccProtected) != 0)
175                                 if ((accessorBits & AccPrivate) != 0)
176                                         modifiers ^= AccPrivate;
177                 }
178
179                 // check for modifiers incompatible with abstract modifier
180                 if ((modifiers & AccAbstract) != 0) {
181                         int incompatibleWithAbstract =
182                                 AccPrivate | AccStatic | AccFinal;// | AccSynchronized | AccNative | AccStrictfp;
183                         if ((modifiers & incompatibleWithAbstract) != 0)
184                                 problemReporter().illegalAbstractModifierCombinationForMethod(
185                                         methodBinding.declaringClass,
186                                         (AbstractMethodDeclaration) referenceContext);
187                         if (!methodBinding.declaringClass.isAbstract())
188                                 problemReporter().abstractMethodInAbstractClass(
189                                         (SourceTypeBinding) methodBinding.declaringClass,
190                                         (AbstractMethodDeclaration) referenceContext);
191                 }
192
193                 /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final)
194                 // methods from a final class are final : 8.4.3.3 
195                 if (methodBinding.declaringClass.isFinal())
196                         modifiers |= AccFinal;
197                 */
198                 // native methods cannot also be tagged as strictfp
199 //              if ((modifiers & AccNative) != 0 && (modifiers & AccStrictfp) != 0)
200 //                      problemReporter().nativeMethodsCannotBeStrictfp(
201 //                              methodBinding.declaringClass,
202 //                              (AbstractMethodDeclaration) referenceContext);
203
204                 // static members are only authorized in a static member or top level type
205                 if (((realModifiers & AccStatic) != 0)
206                         && methodBinding.declaringClass.isNestedType()
207                         && !methodBinding.declaringClass.isStatic())
208                         problemReporter().unexpectedStaticModifierForMethod(
209                                 methodBinding.declaringClass,
210                                 (AbstractMethodDeclaration) referenceContext);
211
212                 methodBinding.modifiers = modifiers;
213         }
214         
215         /* Compute variable positions in scopes given an initial position offset
216          * ignoring unused local variables.
217          * 
218          * Deal with arguments here, locals and subscopes are processed in BlockScope method
219          */
220 //      public void computeLocalVariablePositions(int initOffset, CodeStream codeStream) {
221 //
222 //              boolean isReportingUnusedArgument = false;
223 //
224 //              if (referenceContext instanceof AbstractMethodDeclaration) {
225 //                      AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext;
226 //                      MethodBinding method = methodDecl.binding;
227 //                      CompilerOptions options = compilationUnitScope().environment.options;
228 //                      if (!(method.isAbstract()
229 //                                      || (method.isImplementing() && !options.reportUnusedParameterWhenImplementingAbstract) 
230 //                                      || (method.isOverriding() && !method.isImplementing() && !options.reportUnusedParameterWhenOverridingConcrete)
231 //                                      || method.isMain())) {
232 //                              isReportingUnusedArgument = true;
233 //                      }
234 //              }
235 //              this.offset = initOffset;
236 //              this.maxOffset = initOffset;
237 //
238 //              // manage arguments     
239 //              int ilocal = 0, maxLocals = this.localIndex;    
240 //              while (ilocal < maxLocals) {
241 //                      LocalVariableBinding local = locals[ilocal];
242 //                      if (local == null || !local.isArgument) break; // done with arguments
243 //
244 //                      // do not report fake used variable
245 //                      if (isReportingUnusedArgument
246 //                                      && local.useFlag == LocalVariableBinding.UNUSED
247 //                                      && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable
248 //                              this.problemReporter().unusedArgument(local.declaration);
249 //                      }
250 //
251 //                      // record user-defined argument for attribute generation
252 //                      codeStream.record(local); 
253 //
254 //                      // assign variable position
255 //                      local.resolvedPosition = this.offset;
256 //
257 //                      if ((local.type == LongBinding) || (local.type == DoubleBinding)) {
258 //                              this.offset += 2;
259 //                      } else {
260 //                              this.offset++;
261 //                      }
262 //                      // check for too many arguments/local variables
263 //                      if (this.offset > 0xFF) { // no more than 255 words of arguments
264 //                              this.problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration);
265 //                      }
266 //                      ilocal++;
267 //              }
268 //              
269 //              // sneak in extra argument before other local variables
270 //              if (extraSyntheticArguments != null) {
271 //                      for (int iarg = 0, maxArguments = extraSyntheticArguments.length; iarg < maxArguments; iarg++){
272 //                              SyntheticArgumentBinding argument = extraSyntheticArguments[iarg];
273 //                              argument.resolvedPosition = this.offset;
274 //                              if ((argument.type == LongBinding) || (argument.type == DoubleBinding)){
275 //                                      this.offset += 2;
276 //                              } else {
277 //                                      this.offset++;
278 //                              }
279 //                              if (this.offset > 0xFF) { // no more than 255 words of arguments
280 //                                      this.problemReporter().noMoreAvailableSpaceForArgument(argument, (ASTNode)this.referenceContext); 
281 //                              }
282 //                      }
283 //              }
284 //              this.computeLocalVariablePositions(ilocal, this.offset, codeStream);
285 //      }
286
287         /* Error management:
288          *              keep null for all the errors that prevent the method to be created
289          *              otherwise return a correct method binding (but without the element
290          *              that caused the problem) : ie : Incorrect thrown exception
291          */
292         MethodBinding createMethod(AbstractMethodDeclaration method) {
293
294                 // is necessary to ensure error reporting
295                 this.referenceContext = method;
296                 method.scope = this;
297                 SourceTypeBinding declaringClass = referenceType().binding;
298                 int modifiers = method.modifiers | AccUnresolved;
299                 if (method.isConstructor()) {
300                         method.binding = new MethodBinding(modifiers, null, null, declaringClass);
301                         checkAndSetModifiersForConstructor(method.binding);
302                 } else {
303                         if (declaringClass.isInterface())
304                                 modifiers |= AccPublic | AccAbstract;
305                         method.binding =
306                                 new MethodBinding(modifiers, method.selector, null, null, null, declaringClass);
307                         checkAndSetModifiersForMethod(method.binding);
308                 }
309
310                 this.isStatic = method.binding.isStatic();
311                 return method.binding;
312         }
313
314         /* Overridden to detect the error case inside an explicit constructor call:
315         
316         class X {
317                 int i;
318                 X myX;
319                 X(X x) {
320                         this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors
321                 }
322         }
323         */
324         public FieldBinding findField(
325                 TypeBinding receiverType,
326                 char[] fieldName,
327                 InvocationSite invocationSite) {
328
329                 FieldBinding field = super.findField(receiverType, fieldName, invocationSite);
330                 if (field == null)
331                         return null;
332                 if (!field.isValidBinding())
333                         return field; // answer the error field
334                 if (field.isStatic())
335                         return field; // static fields are always accessible
336
337                 if (!isConstructorCall || receiverType != enclosingSourceType())
338                         return field;
339
340                 if (invocationSite instanceof SingleNameReference)
341                         return new ProblemFieldBinding(
342                                 field.declaringClass,
343                                 fieldName,
344                                 NonStaticReferenceInConstructorInvocation);
345                 if (invocationSite instanceof QualifiedNameReference) {
346                         // look to see if the field is the first binding
347                         QualifiedNameReference name = (QualifiedNameReference) invocationSite;
348                         if (name.binding == null)
349                                 // only true when the field is the fieldbinding at the beginning of name's tokens
350                                 return new ProblemFieldBinding(
351                                         field.declaringClass,
352                                         fieldName,
353                                         NonStaticReferenceInConstructorInvocation);
354                 }
355                 return field;
356         }
357
358         public boolean isInsideInitializer() {
359
360                 return (referenceContext instanceof TypeDeclaration);
361         }
362
363         public boolean isInsideInitializerOrConstructor() {
364
365                 return (referenceContext instanceof TypeDeclaration)
366                         || (referenceContext instanceof ConstructorDeclaration);
367         }
368
369         /* Answer the problem reporter to use for raising new problems.
370          *
371          * Note that as a side-effect, this updates the current reference context
372          * (unit, type or method) in case the problem handler decides it is necessary
373          * to abort.
374          */
375         public ProblemReporter problemReporter() {
376
377                 MethodScope outerMethodScope;
378                 if ((outerMethodScope = outerMostMethodScope()) == this) {
379                         ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
380                         problemReporter.referenceContext = referenceContext;
381                         return problemReporter;
382                 } else {
383                         return outerMethodScope.problemReporter();
384                 }
385         }
386
387         public final int recordInitializationStates(FlowInfo flowInfo) {
388
389                 if (!flowInfo.isReachable()) return -1;
390
391                 UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
392                 long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
393                 long inits = unconditionalFlowInfo.definiteInits;
394                 checkNextEntry : for (int i = lastIndex; --i >= 0;) {
395                         if (definiteInits[i] == inits) {
396                                 long[] otherInits = extraDefiniteInits[i];
397                                 if ((extraInits != null) && (otherInits != null)) {
398                                         if (extraInits.length == otherInits.length) {
399                                                 int j, max;
400                                                 for (j = 0, max = extraInits.length; j < max; j++) {
401                                                         if (extraInits[j] != otherInits[j]) {
402                                                                 continue checkNextEntry;
403                                                         }
404                                                 }
405                                                 return i;
406                                         }
407                                 } else {
408                                         if ((extraInits == null) && (otherInits == null)) {
409                                                 return i;
410                                         }
411                                 }
412                         }
413                 }
414
415                 // add a new entry
416                 if (definiteInits.length == lastIndex) {
417                         // need a resize
418                         System.arraycopy(
419                                 definiteInits,
420                                 0,
421                                 (definiteInits = new long[lastIndex + 20]),
422                                 0,
423                                 lastIndex);
424                         System.arraycopy(
425                                 extraDefiniteInits,
426                                 0,
427                                 (extraDefiniteInits = new long[lastIndex + 20][]),
428                                 0,
429                                 lastIndex);
430                 }
431                 definiteInits[lastIndex] = inits;
432                 if (extraInits != null) {
433                         extraDefiniteInits[lastIndex] = new long[extraInits.length];
434                         System.arraycopy(
435                                 extraInits,
436                                 0,
437                                 extraDefiniteInits[lastIndex],
438                                 0,
439                                 extraInits.length);
440                 }
441                 return lastIndex++;
442         }
443
444         /* Answer the reference type of this scope.
445         *
446         * It is the nearest enclosing type of this scope.
447         */
448         public TypeDeclaration referenceType() {
449
450                 return (TypeDeclaration) ((ClassScope) parent).referenceContext;
451         }
452
453         String basicToString(int tab) {
454
455                 String newLine = "\n"; //$NON-NLS-1$
456                 for (int i = tab; --i >= 0;)
457                         newLine += "\t"; //$NON-NLS-1$
458
459                 String s = newLine + "--- Method Scope ---"; //$NON-NLS-1$
460                 newLine += "\t"; //$NON-NLS-1$
461                 s += newLine + "locals:"; //$NON-NLS-1$
462                 for (int i = 0; i < localIndex; i++)
463                         s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
464                 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
465                 s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$
466                 s += newLine + "initializedField = " + initializedField; //$NON-NLS-1$
467                 s += newLine + "fieldDeclarationIndex = " + fieldDeclarationIndex; //$NON-NLS-1$
468                 s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$
469                 return s;
470         }
471
472 }