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