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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.lookup;
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.codegen.CodeStream;
20 import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
21 import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
22 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
23 import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
24 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
27 * Particular block scope used for methods, constructors or clinits, representing
28 * its outermost blockscope. Note also that such a scope will be provided to enclose
29 * field initializers subscopes as well.
31 public class MethodScope extends BlockScope {
33 public ReferenceContext referenceContext;
34 public boolean isStatic; // method modifier or initializer one
36 //fields used during name resolution
37 public boolean isConstructorCall = false;
38 public FieldBinding initializedField; // the field being initialized
39 public int lastVisibleFieldID = -1; // the ID of the last field which got declared
40 // note that #initializedField can be null AND lastVisibleFieldID >= 0, when processing instance field initializers.
43 public int analysisIndex; // for setting flow-analysis id
44 public boolean isPropagatingInnerClassEmulation;
46 // for local variables table attributes
47 public int lastIndex = 0;
48 public long[] definiteInits = new long[4];
49 public long[][] extraDefiniteInits = new long[4][];
52 public SyntheticArgumentBinding[] extraSyntheticArguments;
54 public MethodScope(ClassScope parent, ReferenceContext context, boolean isStatic) {
56 super(METHOD_SCOPE, parent);
57 locals = new LocalVariableBinding[5];
58 this.referenceContext = context;
59 this.isStatic = isStatic;
65 private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
67 int modifiers = methodBinding.modifiers;
68 if ((modifiers & AccAlternateModifierProblem) != 0)
69 problemReporter().duplicateModifierForMethod(
70 methodBinding.declaringClass,
71 (AbstractMethodDeclaration) referenceContext);
73 if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
74 if (methodBinding.declaringClass.isPublic())
75 modifiers |= AccPublic;
76 else if (methodBinding.declaringClass.isProtected())
77 modifiers |= AccProtected;
80 // after this point, tests on the 16 bits reserved.
81 int realModifiers = modifiers & AccJustFlag;
83 // check for abnormal modifiers
84 int unexpectedModifiers =
85 ~(AccPublic | AccPrivate | AccProtected | AccStrictfp);
86 if ((realModifiers & unexpectedModifiers) != 0)
87 problemReporter().illegalModifierForMethod(
88 methodBinding.declaringClass,
89 (AbstractMethodDeclaration) referenceContext);
91 (((AbstractMethodDeclaration) referenceContext).modifiers & AccStrictfp) != 0)
92 // must check the parse node explicitly
93 problemReporter().illegalModifierForMethod(
94 methodBinding.declaringClass,
95 (AbstractMethodDeclaration) referenceContext);
97 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
98 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
99 if ((accessorBits & (accessorBits - 1)) != 0) {
100 problemReporter().illegalVisibilityModifierCombinationForMethod(
101 methodBinding.declaringClass,
102 (AbstractMethodDeclaration) referenceContext);
104 // need to keep the less restrictive
105 if ((accessorBits & AccPublic) != 0) {
106 if ((accessorBits & AccProtected) != 0)
107 modifiers ^= AccProtected;
108 if ((accessorBits & AccPrivate) != 0)
109 modifiers ^= AccPrivate;
111 if ((accessorBits & AccProtected) != 0)
112 if ((accessorBits & AccPrivate) != 0)
113 modifiers ^= AccPrivate;
116 // 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)
117 if (methodBinding.declaringClass.isPrivate())
118 if ((modifiers & AccPrivate) != 0)
119 modifiers ^= AccPrivate;
121 methodBinding.modifiers = modifiers;
124 /* Spec : 8.4.3 & 9.4
126 private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
128 int modifiers = methodBinding.modifiers;
129 if ((modifiers & AccAlternateModifierProblem) != 0)
130 problemReporter().duplicateModifierForMethod(
131 methodBinding.declaringClass,
132 (AbstractMethodDeclaration) referenceContext);
134 // after this point, tests on the 16 bits reserved.
135 int realModifiers = modifiers & AccJustFlag;
137 // set the requested modifiers for a method in an interface
138 if (methodBinding.declaringClass.isInterface()) {
139 if ((realModifiers & ~(AccPublic | AccAbstract)) != 0)
140 problemReporter().illegalModifierForInterfaceMethod(
141 methodBinding.declaringClass,
142 (AbstractMethodDeclaration) referenceContext);
146 // check for abnormal modifiers
147 int unexpectedModifiers =
158 if ((realModifiers & unexpectedModifiers) != 0)
159 problemReporter().illegalModifierForMethod(
160 methodBinding.declaringClass,
161 (AbstractMethodDeclaration) referenceContext);
163 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
164 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
165 if ((accessorBits & (accessorBits - 1)) != 0) {
166 problemReporter().illegalVisibilityModifierCombinationForMethod(
167 methodBinding.declaringClass,
168 (AbstractMethodDeclaration) referenceContext);
170 // need to keep the less restrictive
171 if ((accessorBits & AccPublic) != 0) {
172 if ((accessorBits & AccProtected) != 0)
173 modifiers ^= AccProtected;
174 if ((accessorBits & AccPrivate) != 0)
175 modifiers ^= AccPrivate;
177 if ((accessorBits & AccProtected) != 0)
178 if ((accessorBits & AccPrivate) != 0)
179 modifiers ^= AccPrivate;
182 // check for modifiers incompatible with abstract modifier
183 if ((modifiers & AccAbstract) != 0) {
184 int incompatibleWithAbstract =
185 AccPrivate | AccStatic | AccFinal | AccSynchronized | AccNative | AccStrictfp;
186 if ((modifiers & incompatibleWithAbstract) != 0)
187 problemReporter().illegalAbstractModifierCombinationForMethod(
188 methodBinding.declaringClass,
189 (AbstractMethodDeclaration) referenceContext);
190 if (!methodBinding.declaringClass.isAbstract())
191 problemReporter().abstractMethodInAbstractClass(
192 (SourceTypeBinding) methodBinding.declaringClass,
193 (AbstractMethodDeclaration) referenceContext);
196 /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final)
197 // methods from a final class are final : 8.4.3.3
198 if (methodBinding.declaringClass.isFinal())
199 modifiers |= AccFinal;
201 // native methods cannot also be tagged as strictfp
202 if ((modifiers & AccNative) != 0 && (modifiers & AccStrictfp) != 0)
203 problemReporter().nativeMethodsCannotBeStrictfp(
204 methodBinding.declaringClass,
205 (AbstractMethodDeclaration) referenceContext);
207 // static members are only authorized in a static member or top level type
208 if (((realModifiers & AccStatic) != 0)
209 && methodBinding.declaringClass.isNestedType()
210 && !methodBinding.declaringClass.isStatic())
211 problemReporter().unexpectedStaticModifierForMethod(
212 methodBinding.declaringClass,
213 (AbstractMethodDeclaration) referenceContext);
215 methodBinding.modifiers = modifiers;
218 /* Compute variable positions in scopes given an initial position offset
219 * ignoring unused local variables.
221 * Deal with arguments here, locals and subscopes are processed in BlockScope method
223 public void computeLocalVariablePositions(int initOffset, CodeStream codeStream) {
225 boolean isReportingUnusedArgument = false;
227 if (referenceContext instanceof AbstractMethodDeclaration) {
228 AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext;
229 MethodBinding method = methodDecl.binding;
230 CompilerOptions options = compilationUnitScope().environment.options;
231 if (!(method.isAbstract()
232 || (method.isImplementing() && !options.reportUnusedParameterWhenImplementingAbstract)
233 || (method.isOverriding() && !method.isImplementing() && !options.reportUnusedParameterWhenOverridingConcrete)
234 || method.isMain())) {
235 isReportingUnusedArgument = true;
238 this.offset = initOffset;
239 this.maxOffset = initOffset;
242 int ilocal = 0, maxLocals = this.localIndex;
243 while (ilocal < maxLocals) {
244 LocalVariableBinding local = locals[ilocal];
245 if (local == null || !local.isArgument) break; // done with arguments
247 // do not report fake used variable
248 if (isReportingUnusedArgument
249 && local.useFlag == LocalVariableBinding.UNUSED
250 && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable
251 this.problemReporter().unusedArgument(local.declaration);
254 // record user-defined argument for attribute generation
255 codeStream.record(local);
257 // assign variable position
258 local.resolvedPosition = this.offset;
260 if ((local.type == LongBinding) || (local.type == DoubleBinding)) {
265 // check for too many arguments/local variables
266 if (this.offset > 0xFF) { // no more than 255 words of arguments
267 this.problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration);
272 // sneak in extra argument before other local variables
273 if (extraSyntheticArguments != null) {
274 for (int iarg = 0, maxArguments = extraSyntheticArguments.length; iarg < maxArguments; iarg++){
275 SyntheticArgumentBinding argument = extraSyntheticArguments[iarg];
276 argument.resolvedPosition = this.offset;
277 if ((argument.type == LongBinding) || (argument.type == DoubleBinding)){
282 if (this.offset > 0xFF) { // no more than 255 words of arguments
283 this.problemReporter().noMoreAvailableSpaceForArgument(argument, (ASTNode)this.referenceContext);
287 this.computeLocalVariablePositions(ilocal, this.offset, codeStream);
291 * keep null for all the errors that prevent the method to be created
292 * otherwise return a correct method binding (but without the element
293 * that caused the problem) : ie : Incorrect thrown exception
295 MethodBinding createMethod(AbstractMethodDeclaration method) {
297 // is necessary to ensure error reporting
298 this.referenceContext = method;
300 SourceTypeBinding declaringClass = referenceType().binding;
301 int modifiers = method.modifiers | AccUnresolved;
302 if (method.isConstructor()) {
303 if (method.isDefaultConstructor()) {
304 modifiers |= AccIsDefaultConstructor;
306 method.binding = new MethodBinding(modifiers, null, null, declaringClass);
307 checkAndSetModifiersForConstructor(method.binding);
309 if (declaringClass.isInterface())
310 modifiers |= AccPublic | AccAbstract;
312 new MethodBinding(modifiers, method.selector, null, null, null, declaringClass);
313 checkAndSetModifiersForMethod(method.binding);
315 this.isStatic = method.binding.isStatic();
316 return method.binding;
319 /* Overridden to detect the error case inside an explicit constructor call:
325 this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors
329 public FieldBinding findField(
330 TypeBinding receiverType,
332 InvocationSite invocationSite,
333 boolean needResolve) {
335 FieldBinding field = super.findField(receiverType, fieldName, invocationSite, needResolve);
338 if (!field.isValidBinding())
339 return field; // answer the error field
340 if (field.isStatic())
341 return field; // static fields are always accessible
343 if (!isConstructorCall || receiverType != enclosingSourceType())
346 if (invocationSite instanceof SingleNameReference)
347 return new ProblemFieldBinding(
348 field, // closest match
349 field.declaringClass,
351 NonStaticReferenceInConstructorInvocation);
352 if (invocationSite instanceof QualifiedNameReference) {
353 // look to see if the field is the first binding
354 QualifiedNameReference name = (QualifiedNameReference) invocationSite;
355 if (name.binding == null)
356 // only true when the field is the fieldbinding at the beginning of name's tokens
357 return new ProblemFieldBinding(
358 field, // closest match
359 field.declaringClass,
361 NonStaticReferenceInConstructorInvocation);
366 public boolean isInsideConstructor() {
368 return (referenceContext instanceof ConstructorDeclaration);
371 public boolean isInsideInitializer() {
373 return (referenceContext instanceof TypeDeclaration);
376 public boolean isInsideInitializerOrConstructor() {
378 return (referenceContext instanceof TypeDeclaration)
379 || (referenceContext instanceof ConstructorDeclaration);
382 /* Answer the problem reporter to use for raising new problems.
384 * Note that as a side-effect, this updates the current reference context
385 * (unit, type or method) in case the problem handler decides it is necessary
388 public ProblemReporter problemReporter() {
390 MethodScope outerMethodScope;
391 if ((outerMethodScope = outerMostMethodScope()) == this) {
392 ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
393 problemReporter.referenceContext = referenceContext;
394 return problemReporter;
396 return outerMethodScope.problemReporter();
399 public final int recordInitializationStates(FlowInfo flowInfo) {
401 if (!flowInfo.isReachable()) return -1;
403 UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
404 long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
405 long inits = unconditionalFlowInfo.definiteInits;
406 checkNextEntry : for (int i = lastIndex; --i >= 0;) {
407 if (definiteInits[i] == inits) {
408 long[] otherInits = extraDefiniteInits[i];
409 if ((extraInits != null) && (otherInits != null)) {
410 if (extraInits.length == otherInits.length) {
412 for (j = 0, max = extraInits.length; j < max; j++) {
413 if (extraInits[j] != otherInits[j]) {
414 continue checkNextEntry;
420 if ((extraInits == null) && (otherInits == null)) {
428 if (definiteInits.length == lastIndex) {
433 (definiteInits = new long[lastIndex + 20]),
439 (extraDefiniteInits = new long[lastIndex + 20][]),
443 definiteInits[lastIndex] = inits;
444 if (extraInits != null) {
445 extraDefiniteInits[lastIndex] = new long[extraInits.length];
449 extraDefiniteInits[lastIndex],
456 /* Answer the reference method of this scope, or null if initialization scoope.
458 public AbstractMethodDeclaration referenceMethod() {
460 if (referenceContext instanceof AbstractMethodDeclaration) return (AbstractMethodDeclaration) referenceContext;
464 /* Answer the reference type of this scope.
466 * It is the nearest enclosing type of this scope.
468 public TypeDeclaration referenceType() {
470 return ((ClassScope) parent).referenceContext;
473 String basicToString(int tab) {
475 String newLine = "\n"; //$NON-NLS-1$
476 for (int i = tab; --i >= 0;)
477 newLine += "\t"; //$NON-NLS-1$
479 String s = newLine + "--- Method Scope ---"; //$NON-NLS-1$
480 newLine += "\t"; //$NON-NLS-1$
481 s += newLine + "locals:"; //$NON-NLS-1$
482 for (int i = 0; i < localIndex; i++)
483 s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
484 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
485 s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$
486 s += newLine + "initializedField = " + initializedField; //$NON-NLS-1$
487 s += newLine + "lastVisibleFieldID = " + lastVisibleFieldID; //$NON-NLS-1$
488 s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$