intial version
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / MethodScope.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.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 needToCompactLocalVariables;
32         public boolean isStatic; // method modifier or initializer one
33
34         //fields used in the TC process (no real meaning)
35         public static final int NotInFieldDecl = -1; //must be a negative value 
36         public boolean isConstructorCall = false; //modified on the fly by the TC
37         public int fieldDeclarationIndex = NotInFieldDecl;
38         //modified on the fly by the TC
39
40         public int analysisIndex; // for setting flow-analysis id
41
42         public boolean isPropagatingInnerClassEmulation;
43
44         // for local variables table attributes
45         public int lastIndex = 0;
46         public long[] definiteInits = new long[4];
47         public long[][] extraDefiniteInits = new long[4][];
48
49         public MethodScope(
50                 ClassScope parent,
51                 ReferenceContext context,
52                 boolean isStatic) {
53
54                 super(METHOD_SCOPE, parent);
55                 locals = new LocalVariableBinding[5];
56                 this.referenceContext = context;
57                 this.isStatic = isStatic;
58                 this.startIndex = 0;
59         }
60
61         /* Spec : 8.4.3 & 9.4
62          */
63         private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
64                 
65                 int modifiers = methodBinding.modifiers;
66                 if ((modifiers & AccAlternateModifierProblem) != 0)
67                         problemReporter().duplicateModifierForMethod(
68                                 methodBinding.declaringClass,
69                                 (AbstractMethodDeclaration) referenceContext);
70
71                 if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
72                         if (methodBinding.declaringClass.isPublic())
73                                 modifiers |= AccPublic;
74                         else if (methodBinding.declaringClass.isProtected())
75                                 modifiers |= AccProtected;
76                 }
77
78                 // after this point, tests on the 16 bits reserved.
79                 int realModifiers = modifiers & AccJustFlag;
80
81                 // check for abnormal modifiers
82                 int unexpectedModifiers =
83                         ~(AccPublic | AccPrivate | AccProtected | AccStrictfp);
84                 if ((realModifiers & unexpectedModifiers) != 0)
85                         problemReporter().illegalModifierForMethod(
86                                 methodBinding.declaringClass,
87                                 (AbstractMethodDeclaration) referenceContext);
88                 else if (
89                         (((AbstractMethodDeclaration) referenceContext).modifiers & AccStrictfp) != 0)
90                         // must check the parse node explicitly
91                         problemReporter().illegalModifierForMethod(
92                                 methodBinding.declaringClass,
93                                 (AbstractMethodDeclaration) referenceContext);
94
95                 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
96                 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
97                 if ((accessorBits & (accessorBits - 1)) != 0) {
98                         problemReporter().illegalVisibilityModifierCombinationForMethod(
99                                 methodBinding.declaringClass,
100                                 (AbstractMethodDeclaration) referenceContext);
101
102                         // need to keep the less restrictive
103                         if ((accessorBits & AccPublic) != 0) {
104                                 if ((accessorBits & AccProtected) != 0)
105                                         modifiers ^= AccProtected;
106                                 if ((accessorBits & AccPrivate) != 0)
107                                         modifiers ^= AccPrivate;
108                         }
109                         if ((accessorBits & AccProtected) != 0)
110                                 if ((accessorBits & AccPrivate) != 0)
111                                         modifiers ^= AccPrivate;
112                 }
113
114                 // 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)
115                 if (methodBinding.declaringClass.isPrivate())
116                         if ((modifiers & AccPrivate) != 0)
117                                 modifiers ^= AccPrivate;
118
119                 methodBinding.modifiers = modifiers;
120         }
121         
122         /* Spec : 8.4.3 & 9.4
123          */
124         private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
125                 
126                 int modifiers = methodBinding.modifiers;
127                 if ((modifiers & AccAlternateModifierProblem) != 0)
128                         problemReporter().duplicateModifierForMethod(
129                                 methodBinding.declaringClass,
130                                 (AbstractMethodDeclaration) referenceContext);
131
132                 // after this point, tests on the 16 bits reserved.
133                 int realModifiers = modifiers & AccJustFlag;
134
135                 // set the requested modifiers for a method in an interface
136                 if (methodBinding.declaringClass.isInterface()) {
137                         if ((realModifiers & ~(AccPublic | AccAbstract)) != 0)
138                                 problemReporter().illegalModifierForInterfaceMethod(
139                                         methodBinding.declaringClass,
140                                         (AbstractMethodDeclaration) referenceContext);
141                         return;
142                 }
143
144                 // check for abnormal modifiers
145                 int unexpectedModifiers =
146                         ~(
147                                 AccPublic
148                                         | AccPrivate
149                                         | AccProtected
150                                         | AccAbstract
151                                         | AccStatic
152                                         | AccFinal
153                                         | AccSynchronized
154                                         | AccNative
155                                         | AccStrictfp);
156                 if ((realModifiers & unexpectedModifiers) != 0)
157                         problemReporter().illegalModifierForMethod(
158                                 methodBinding.declaringClass,
159                                 (AbstractMethodDeclaration) referenceContext);
160
161                 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
162                 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
163                 if ((accessorBits & (accessorBits - 1)) != 0) {
164                         problemReporter().illegalVisibilityModifierCombinationForMethod(
165                                 methodBinding.declaringClass,
166                                 (AbstractMethodDeclaration) referenceContext);
167
168                         // need to keep the less restrictive
169                         if ((accessorBits & AccPublic) != 0) {
170                                 if ((accessorBits & AccProtected) != 0)
171                                         modifiers ^= AccProtected;
172                                 if ((accessorBits & AccPrivate) != 0)
173                                         modifiers ^= AccPrivate;
174                         }
175                         if ((accessorBits & AccProtected) != 0)
176                                 if ((accessorBits & AccPrivate) != 0)
177                                         modifiers ^= AccPrivate;
178                 }
179
180                 // check for modifiers incompatible with abstract modifier
181                 if ((modifiers & AccAbstract) != 0) {
182                         int incompatibleWithAbstract =
183                                 AccPrivate | AccStatic | AccFinal | AccSynchronized | AccNative | AccStrictfp;
184                         if ((modifiers & incompatibleWithAbstract) != 0)
185                                 problemReporter().illegalAbstractModifierCombinationForMethod(
186                                         methodBinding.declaringClass,
187                                         (AbstractMethodDeclaration) referenceContext);
188                         if (!methodBinding.declaringClass.isAbstract())
189                                 problemReporter().abstractMethodInAbstractClass(
190                                         (SourceTypeBinding) methodBinding.declaringClass,
191                                         (AbstractMethodDeclaration) referenceContext);
192                 }
193
194                 /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final)
195                 // methods from a final class are final : 8.4.3.3 
196                 if (methodBinding.declaringClass.isFinal())
197                         modifiers |= AccFinal;
198                 */
199                 // native methods cannot also be tagged as strictfp
200                 if ((modifiers & AccNative) != 0 && (modifiers & AccStrictfp) != 0)
201                         problemReporter().nativeMethodsCannotBeStrictfp(
202                                 methodBinding.declaringClass,
203                                 (AbstractMethodDeclaration) referenceContext);
204
205                 // static members are only authorized in a static member or top level type
206                 if (((realModifiers & AccStatic) != 0)
207                         && methodBinding.declaringClass.isNestedType()
208                         && !methodBinding.declaringClass.isStatic())
209                         problemReporter().unexpectedStaticModifierForMethod(
210                                 methodBinding.declaringClass,
211                                 (AbstractMethodDeclaration) referenceContext);
212
213                 methodBinding.modifiers = modifiers;
214         }
215         
216         /* Error management:
217          *              keep null for all the errors that prevent the method to be created
218          *              otherwise return a correct method binding (but without the element
219          *              that caused the problem) : ie : Incorrect thrown exception
220          */
221         MethodBinding createMethod(AbstractMethodDeclaration method) {
222
223                 // is necessary to ensure error reporting
224                 this.referenceContext = method;
225                 method.scope = this;
226                 SourceTypeBinding declaringClass = referenceType().binding;
227                 int modifiers = method.modifiers | AccUnresolved;
228                 if (method.isConstructor()) {
229                         method.binding = new MethodBinding(modifiers, null, null, declaringClass);
230                         checkAndSetModifiersForConstructor(method.binding);
231                 } else {
232                         if (declaringClass.isInterface())
233                                 modifiers |= AccPublic | AccAbstract;
234                         method.binding =
235                                 new MethodBinding(modifiers, method.selector, null, null, null, declaringClass);
236                         checkAndSetModifiersForMethod(method.binding);
237                 }
238
239                 this.isStatic = method.binding.isStatic();
240                 return method.binding;
241         }
242
243         /* Overridden to detect the error case inside an explicit constructor call:
244         
245         class X {
246                 int i;
247                 X myX;
248                 X(X x) {
249                         this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors
250                 }
251         }
252         */
253         public FieldBinding findField(
254                 TypeBinding receiverType,
255                 char[] fieldName,
256                 InvocationSite invocationSite) {
257
258                 FieldBinding field = super.findField(receiverType, fieldName, invocationSite);
259                 if (field == null)
260                         return null;
261                 if (!field.isValidBinding())
262                         return field; // answer the error field
263                 if (field.isStatic())
264                         return field; // static fields are always accessible
265
266                 if (!isConstructorCall || receiverType != enclosingSourceType())
267                         return field;
268
269                 if (invocationSite instanceof SingleNameReference)
270                         return new ProblemFieldBinding(
271                                 field.declaringClass,
272                                 fieldName,
273                                 NonStaticReferenceInConstructorInvocation);
274                 if (invocationSite instanceof QualifiedNameReference) {
275                         // look to see if the field is the first binding
276                         QualifiedNameReference name = (QualifiedNameReference) invocationSite;
277                         if (name.binding == null)
278                                 // only true when the field is the fieldbinding at the beginning of name's tokens
279                                 return new ProblemFieldBinding(
280                                         field.declaringClass,
281                                         fieldName,
282                                         NonStaticReferenceInConstructorInvocation);
283                 }
284                 return field;
285         }
286
287         public boolean isInsideInitializer() {
288
289                 return (referenceContext instanceof TypeDeclaration);
290         }
291
292         public boolean isInsideInitializerOrConstructor() {
293
294                 return (referenceContext instanceof TypeDeclaration)
295                         || (referenceContext instanceof ConstructorDeclaration);
296         }
297
298         /* Answer the problem reporter to use for raising new problems.
299          *
300          * Note that as a side-effect, this updates the current reference context
301          * (unit, type or method) in case the problem handler decides it is necessary
302          * to abort.
303          */
304         public ProblemReporter problemReporter() {
305
306                 MethodScope outerMethodScope;
307                 if ((outerMethodScope = outerMostMethodScope()) == this) {
308                         ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
309                         problemReporter.referenceContext = referenceContext;
310                         return problemReporter;
311                 } else {
312                         return outerMethodScope.problemReporter();
313                 }
314         }
315
316         public final int recordInitializationStates(FlowInfo flowInfo) {
317
318                 if ((flowInfo == FlowInfo.DeadEnd) || (flowInfo.isFakeReachable())) {
319                         return -1;
320                 }
321                 UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
322                 long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
323                 long inits = unconditionalFlowInfo.definiteInits;
324                 checkNextEntry : for (int i = lastIndex; --i >= 0;) {
325                         if (definiteInits[i] == inits) {
326                                 long[] otherInits = extraDefiniteInits[i];
327                                 if ((extraInits != null) && (otherInits != null)) {
328                                         if (extraInits.length == otherInits.length) {
329                                                 int j, max;
330                                                 for (j = 0, max = extraInits.length; j < max; j++) {
331                                                         if (extraInits[j] != otherInits[j]) {
332                                                                 continue checkNextEntry;
333                                                         }
334                                                 }
335                                                 return i;
336                                         }
337                                 } else {
338                                         if ((extraInits == null) && (otherInits == null)) {
339                                                 return i;
340                                         }
341                                 }
342                         }
343                 }
344
345                 // add a new entry
346                 if (definiteInits.length == lastIndex) {
347                         // need a resize
348                         System.arraycopy(
349                                 definiteInits,
350                                 0,
351                                 (definiteInits = new long[lastIndex + 20]),
352                                 0,
353                                 lastIndex);
354                         System.arraycopy(
355                                 extraDefiniteInits,
356                                 0,
357                                 (extraDefiniteInits = new long[lastIndex + 20][]),
358                                 0,
359                                 lastIndex);
360                 }
361                 definiteInits[lastIndex] = inits;
362                 if (extraInits != null) {
363                         extraDefiniteInits[lastIndex] = new long[extraInits.length];
364                         System.arraycopy(
365                                 extraInits,
366                                 0,
367                                 extraDefiniteInits[lastIndex],
368                                 0,
369                                 extraInits.length);
370                 }
371                 return lastIndex++;
372         }
373
374         /* Answer the reference type of this scope.
375         *
376         * i.e. the nearest enclosing type of this scope.
377         */
378         public TypeDeclaration referenceType() {
379
380                 return (TypeDeclaration) ((ClassScope) parent).referenceContext;
381         }
382
383         String basicToString(int tab) {
384
385                 String newLine = "\n"; //$NON-NLS-1$
386                 for (int i = tab; --i >= 0;)
387                         newLine += "\t"; //$NON-NLS-1$
388
389                 String s = newLine + "--- Method Scope ---"; //$NON-NLS-1$
390                 newLine += "\t"; //$NON-NLS-1$
391                 s += newLine + "locals:"; //$NON-NLS-1$
392                 for (int i = 0; i < localIndex; i++)
393                         s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
394                 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
395                 s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$
396                 s += newLine + "fieldDeclarationIndex = " + fieldDeclarationIndex; //$NON-NLS-1$
397                 return s;
398         }
399
400 }