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