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