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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.lookup;
13 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.flow.UnconditionalFlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
18 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
19 import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
20 import net.sourceforge.phpeclipse.internal.compiler.ast.ConstructorDeclaration;
21 import net.sourceforge.phpeclipse.internal.compiler.ast.QualifiedNameReference;
22 import net.sourceforge.phpeclipse.internal.compiler.ast.SingleNameReference;
23 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
26 * Particular block scope used for methods, constructors or clinits, representing
27 * its outermost blockscope. Note also that such a scope will be provided to enclose
28 * field initializers subscopes as well.
30 public class MethodScope extends BlockScope {
32 public ReferenceContext referenceContext;
33 public boolean isStatic; // method modifier or initializer one
35 //fields used during name resolution
36 public static final int NotInFieldDecl = -1; //must be a negative value
37 public boolean isConstructorCall = false;
38 public FieldBinding initializedField; // the field being initialized
39 public int fieldDeclarationIndex = NotInFieldDecl;
42 public int analysisIndex; // for setting flow-analysis id
43 public boolean isPropagatingInnerClassEmulation;
45 // for local variables table attributes
46 public int lastIndex = 0;
47 public long[] definiteInits = new long[4];
48 public long[][] extraDefiniteInits = new long[4][];
51 public SyntheticArgumentBinding[] extraSyntheticArguments;
53 public MethodScope(ClassScope parent, ReferenceContext context, boolean isStatic) {
55 super(METHOD_SCOPE, parent);
56 locals = new LocalVariableBinding[5];
57 this.referenceContext = context;
58 this.isStatic = isStatic;
64 private void checkAndSetModifiersForConstructor(MethodBinding methodBinding) {
66 int modifiers = methodBinding.modifiers;
67 if ((modifiers & AccAlternateModifierProblem) != 0)
68 problemReporter().duplicateModifierForMethod(
69 methodBinding.declaringClass,
70 (AbstractMethodDeclaration) referenceContext);
72 if (((ConstructorDeclaration) referenceContext).isDefaultConstructor) {
73 if (methodBinding.declaringClass.isPublic())
74 modifiers |= AccPublic;
75 else if (methodBinding.declaringClass.isProtected())
76 modifiers |= AccProtected;
79 // after this point, tests on the 16 bits reserved.
80 int realModifiers = modifiers & AccJustFlag;
82 // check for abnormal modifiers
83 int unexpectedModifiers =
84 ~(AccPublic | AccPrivate | AccProtected);// | AccStrictfp);
85 if ((realModifiers & unexpectedModifiers) != 0)
86 problemReporter().illegalModifierForMethod(
87 methodBinding.declaringClass,
88 (AbstractMethodDeclaration) referenceContext);
90 // (((AbstractMethodDeclaration) referenceContext).modifiers & AccStrictfp) != 0)
91 // // must check the parse node explicitly
92 // problemReporter().illegalModifierForMethod(
93 // methodBinding.declaringClass,
94 // (AbstractMethodDeclaration) referenceContext);
96 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
97 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
98 if ((accessorBits & (accessorBits - 1)) != 0) {
99 problemReporter().illegalVisibilityModifierCombinationForMethod(
100 methodBinding.declaringClass,
101 (AbstractMethodDeclaration) referenceContext);
103 // need to keep the less restrictive
104 if ((accessorBits & AccPublic) != 0) {
105 if ((accessorBits & AccProtected) != 0)
106 modifiers ^= AccProtected;
107 if ((accessorBits & AccPrivate) != 0)
108 modifiers ^= AccPrivate;
110 if ((accessorBits & AccProtected) != 0)
111 if ((accessorBits & AccPrivate) != 0)
112 modifiers ^= AccPrivate;
115 // if the receiver's declaring class is a private nested type, then make sure the receiver is not private (causes problems for inner type emulation)
116 if (methodBinding.declaringClass.isPrivate())
117 if ((modifiers & AccPrivate) != 0)
118 modifiers ^= AccPrivate;
120 methodBinding.modifiers = modifiers;
123 /* Spec : 8.4.3 & 9.4
125 private void checkAndSetModifiersForMethod(MethodBinding methodBinding) {
127 int modifiers = methodBinding.modifiers;
128 if ((modifiers & AccAlternateModifierProblem) != 0)
129 problemReporter().duplicateModifierForMethod(
130 methodBinding.declaringClass,
131 (AbstractMethodDeclaration) referenceContext);
133 // after this point, tests on the 16 bits reserved.
134 int realModifiers = modifiers & AccJustFlag;
136 // set the requested modifiers for a method in an interface
137 if (methodBinding.declaringClass.isInterface()) {
138 if ((realModifiers & ~(AccPublic | AccAbstract)) != 0)
139 problemReporter().illegalModifierForInterfaceMethod(
140 methodBinding.declaringClass,
141 (AbstractMethodDeclaration) referenceContext);
145 // check for abnormal modifiers
146 int unexpectedModifiers =
157 if ((realModifiers & unexpectedModifiers) != 0)
158 problemReporter().illegalModifierForMethod(
159 methodBinding.declaringClass,
160 (AbstractMethodDeclaration) referenceContext);
162 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
163 int accessorBits = realModifiers & (AccPublic | AccProtected | AccPrivate);
164 if ((accessorBits & (accessorBits - 1)) != 0) {
165 problemReporter().illegalVisibilityModifierCombinationForMethod(
166 methodBinding.declaringClass,
167 (AbstractMethodDeclaration) referenceContext);
169 // need to keep the less restrictive
170 if ((accessorBits & AccPublic) != 0) {
171 if ((accessorBits & AccProtected) != 0)
172 modifiers ^= AccProtected;
173 if ((accessorBits & AccPrivate) != 0)
174 modifiers ^= AccPrivate;
176 if ((accessorBits & AccProtected) != 0)
177 if ((accessorBits & AccPrivate) != 0)
178 modifiers ^= AccPrivate;
181 // check for modifiers incompatible with abstract modifier
182 if ((modifiers & AccAbstract) != 0) {
183 int incompatibleWithAbstract =
184 AccPrivate | AccStatic | AccFinal;// | AccSynchronized | AccNative | AccStrictfp;
185 if ((modifiers & incompatibleWithAbstract) != 0)
186 problemReporter().illegalAbstractModifierCombinationForMethod(
187 methodBinding.declaringClass,
188 (AbstractMethodDeclaration) referenceContext);
189 if (!methodBinding.declaringClass.isAbstract())
190 problemReporter().abstractMethodInAbstractClass(
191 (SourceTypeBinding) methodBinding.declaringClass,
192 (AbstractMethodDeclaration) referenceContext);
195 /* DISABLED for backward compatibility with javac (if enabled should also mark private methods as final)
196 // methods from a final class are final : 8.4.3.3
197 if (methodBinding.declaringClass.isFinal())
198 modifiers |= AccFinal;
200 // native methods cannot also be tagged as strictfp
201 // if ((modifiers & AccNative) != 0 && (modifiers & AccStrictfp) != 0)
202 // problemReporter().nativeMethodsCannotBeStrictfp(
203 // methodBinding.declaringClass,
204 // (AbstractMethodDeclaration) referenceContext);
206 // static members are only authorized in a static member or top level type
207 if (((realModifiers & AccStatic) != 0)
208 && methodBinding.declaringClass.isNestedType()
209 && !methodBinding.declaringClass.isStatic())
210 problemReporter().unexpectedStaticModifierForMethod(
211 methodBinding.declaringClass,
212 (AbstractMethodDeclaration) referenceContext);
214 methodBinding.modifiers = modifiers;
217 /* Compute variable positions in scopes given an initial position offset
218 * ignoring unused local variables.
220 * Deal with arguments here, locals and subscopes are processed in BlockScope method
222 // public void computeLocalVariablePositions(int initOffset, CodeStream codeStream) {
224 // boolean isReportingUnusedArgument = false;
226 // if (referenceContext instanceof AbstractMethodDeclaration) {
227 // AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext;
228 // MethodBinding method = methodDecl.binding;
229 // CompilerOptions options = compilationUnitScope().environment.options;
230 // if (!(method.isAbstract()
231 // || (method.isImplementing() && !options.reportUnusedParameterWhenImplementingAbstract)
232 // || (method.isOverriding() && !method.isImplementing() && !options.reportUnusedParameterWhenOverridingConcrete)
233 // || method.isMain())) {
234 // isReportingUnusedArgument = true;
237 // this.offset = initOffset;
238 // this.maxOffset = initOffset;
240 // // manage arguments
241 // int ilocal = 0, maxLocals = this.localIndex;
242 // while (ilocal < maxLocals) {
243 // LocalVariableBinding local = locals[ilocal];
244 // if (local == null || !local.isArgument) break; // done with arguments
246 // // do not report fake used variable
247 // if (isReportingUnusedArgument
248 // && local.useFlag == LocalVariableBinding.UNUSED
249 // && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachableMASK) != 0)) { // declaration is reachable
250 // this.problemReporter().unusedArgument(local.declaration);
253 // // record user-defined argument for attribute generation
254 // codeStream.record(local);
256 // // assign variable position
257 // local.resolvedPosition = this.offset;
259 // if ((local.type == LongBinding) || (local.type == DoubleBinding)) {
264 // // check for too many arguments/local variables
265 // if (this.offset > 0xFF) { // no more than 255 words of arguments
266 // this.problemReporter().noMoreAvailableSpaceForArgument(local, local.declaration);
271 // // sneak in extra argument before other local variables
272 // if (extraSyntheticArguments != null) {
273 // for (int iarg = 0, maxArguments = extraSyntheticArguments.length; iarg < maxArguments; iarg++){
274 // SyntheticArgumentBinding argument = extraSyntheticArguments[iarg];
275 // argument.resolvedPosition = this.offset;
276 // if ((argument.type == LongBinding) || (argument.type == DoubleBinding)){
281 // if (this.offset > 0xFF) { // no more than 255 words of arguments
282 // this.problemReporter().noMoreAvailableSpaceForArgument(argument, (ASTNode)this.referenceContext);
286 // this.computeLocalVariablePositions(ilocal, this.offset, codeStream);
290 * keep null for all the errors that prevent the method to be created
291 * otherwise return a correct method binding (but without the element
292 * that caused the problem) : ie : Incorrect thrown exception
294 MethodBinding createMethod(AbstractMethodDeclaration method) {
296 // is necessary to ensure error reporting
297 this.referenceContext = method;
299 SourceTypeBinding declaringClass = referenceType().binding;
300 int modifiers = method.modifiers | AccUnresolved;
301 if (method.isConstructor()) {
302 method.binding = new MethodBinding(modifiers, null, null, declaringClass);
303 checkAndSetModifiersForConstructor(method.binding);
305 if (declaringClass.isInterface())
306 modifiers |= AccPublic | AccAbstract;
308 new MethodBinding(modifiers, method.selector, null, null, null, declaringClass);
309 checkAndSetModifiersForMethod(method.binding);
312 this.isStatic = method.binding.isStatic();
313 return method.binding;
316 /* Overridden to detect the error case inside an explicit constructor call:
322 this(i, myX.i, x.i); // same for super calls... only the first 2 field accesses are errors
326 public FieldBinding findField(
327 TypeBinding receiverType,
329 InvocationSite invocationSite) {
331 FieldBinding field = super.findField(receiverType, fieldName, invocationSite);
334 if (!field.isValidBinding())
335 return field; // answer the error field
336 if (field.isStatic())
337 return field; // static fields are always accessible
339 if (!isConstructorCall || receiverType != enclosingSourceType())
342 if (invocationSite instanceof SingleNameReference)
343 return new ProblemFieldBinding(
344 field.declaringClass,
346 NonStaticReferenceInConstructorInvocation);
347 if (invocationSite instanceof QualifiedNameReference) {
348 // look to see if the field is the first binding
349 QualifiedNameReference name = (QualifiedNameReference) invocationSite;
350 if (name.binding == null)
351 // only true when the field is the fieldbinding at the beginning of name's tokens
352 return new ProblemFieldBinding(
353 field.declaringClass,
355 NonStaticReferenceInConstructorInvocation);
360 public boolean isInsideInitializer() {
362 return (referenceContext instanceof TypeDeclaration);
365 public boolean isInsideInitializerOrConstructor() {
367 return (referenceContext instanceof TypeDeclaration)
368 || (referenceContext instanceof ConstructorDeclaration);
371 /* Answer the problem reporter to use for raising new problems.
373 * Note that as a side-effect, this updates the current reference context
374 * (unit, type or method) in case the problem handler decides it is necessary
377 public ProblemReporter problemReporter() {
379 MethodScope outerMethodScope;
380 if ((outerMethodScope = outerMostMethodScope()) == this) {
381 ProblemReporter problemReporter = referenceCompilationUnit().problemReporter;
382 problemReporter.referenceContext = referenceContext;
383 return problemReporter;
385 return outerMethodScope.problemReporter();
389 public final int recordInitializationStates(FlowInfo flowInfo) {
391 if (!flowInfo.isReachable()) return -1;
393 UnconditionalFlowInfo unconditionalFlowInfo = flowInfo.unconditionalInits();
394 long[] extraInits = unconditionalFlowInfo.extraDefiniteInits;
395 long inits = unconditionalFlowInfo.definiteInits;
396 checkNextEntry : for (int i = lastIndex; --i >= 0;) {
397 if (definiteInits[i] == inits) {
398 long[] otherInits = extraDefiniteInits[i];
399 if ((extraInits != null) && (otherInits != null)) {
400 if (extraInits.length == otherInits.length) {
402 for (j = 0, max = extraInits.length; j < max; j++) {
403 if (extraInits[j] != otherInits[j]) {
404 continue checkNextEntry;
410 if ((extraInits == null) && (otherInits == null)) {
418 if (definiteInits.length == lastIndex) {
423 (definiteInits = new long[lastIndex + 20]),
429 (extraDefiniteInits = new long[lastIndex + 20][]),
433 definiteInits[lastIndex] = inits;
434 if (extraInits != null) {
435 extraDefiniteInits[lastIndex] = new long[extraInits.length];
439 extraDefiniteInits[lastIndex],
446 /* Answer the reference type of this scope.
448 * It is the nearest enclosing type of this scope.
450 public TypeDeclaration referenceType() {
452 return (TypeDeclaration) ((ClassScope) parent).referenceContext;
455 String basicToString(int tab) {
457 String newLine = "\n"; //$NON-NLS-1$
458 for (int i = tab; --i >= 0;)
459 newLine += "\t"; //$NON-NLS-1$
461 String s = newLine + "--- Method Scope ---"; //$NON-NLS-1$
462 newLine += "\t"; //$NON-NLS-1$
463 s += newLine + "locals:"; //$NON-NLS-1$
464 for (int i = 0; i < localIndex; i++)
465 s += newLine + "\t" + locals[i].toString(); //$NON-NLS-1$
466 s += newLine + "startIndex = " + startIndex; //$NON-NLS-1$
467 s += newLine + "isConstructorCall = " + isConstructorCall; //$NON-NLS-1$
468 s += newLine + "initializedField = " + initializedField; //$NON-NLS-1$
469 s += newLine + "fieldDeclarationIndex = " + fieldDeclarationIndex; //$NON-NLS-1$
470 s += newLine + "referenceContext = " + referenceContext; //$NON-NLS-1$