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