Fix nasty bug #706. See trac.
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / SyntheticAccessMethodBinding.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.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
15 import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
16
17 public class SyntheticAccessMethodBinding extends MethodBinding {
18
19         public FieldBinding targetReadField; // read access to a field
20
21         public FieldBinding targetWriteField; // write access to a field
22
23         public MethodBinding targetMethod; // method or constructor
24
25         public int accessType;
26
27         public final static int FieldReadAccess = 1; // field read
28
29         public final static int FieldWriteAccess = 2; // field write
30
31         public final static int MethodAccess = 3; // normal method
32
33         public final static int ConstructorAccess = 4; // constructor
34
35         public final static int SuperMethodAccess = 5; // super method
36
37         final static char[] AccessMethodPrefix = { 'a', 'c', 'c', 'e', 's', 's',
38                         '$' };
39
40         public int sourceStart = 0; // start position of the matching declaration
41
42         public int index; // used for sorting access methods in the class file
43
44         public SyntheticAccessMethodBinding(FieldBinding targetField,
45                         boolean isReadAccess, ReferenceBinding declaringClass) {
46
47                 this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
48                 SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
49                 SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType
50                                 .syntheticAccessMethods();
51                 int methodId = knownAccessMethods == null ? 0
52                                 : knownAccessMethods.length;
53                 this.index = methodId;
54                 this.selector = CharOperation.concat(AccessMethodPrefix, String
55                                 .valueOf(methodId).toCharArray());
56                 if (isReadAccess) {
57                         this.returnType = targetField.type;
58                         if (targetField.isStatic()) {
59                                 this.parameters = NoParameters;
60                         } else {
61                                 this.parameters = new TypeBinding[1];
62                                 this.parameters[0] = declaringSourceType;
63                         }
64                         this.targetReadField = targetField;
65                         this.accessType = FieldReadAccess;
66                 } else {
67                         this.returnType = VoidBinding;
68                         if (targetField.isStatic()) {
69                                 this.parameters = new TypeBinding[1];
70                                 this.parameters[0] = targetField.type;
71                         } else {
72                                 this.parameters = new TypeBinding[2];
73                                 this.parameters[0] = declaringSourceType;
74                                 this.parameters[1] = targetField.type;
75                         }
76                         this.targetWriteField = targetField;
77                         this.accessType = FieldWriteAccess;
78                 }
79                 this.thrownExceptions = NoExceptions;
80                 this.declaringClass = declaringSourceType;
81
82                 // check for method collision
83                 boolean needRename;
84                 do {
85                         check: {
86                                 needRename = false;
87                                 // check for collision with known methods
88                                 MethodBinding[] methods = declaringSourceType.methods;
89                                 for (int i = 0, length = methods.length; i < length; i++) {
90                                         if (this.selector == methods[i].selector
91                                                         && this.areParametersEqual(methods[i])) {
92                                                 needRename = true;
93                                                 break check;
94                                         }
95                                 }
96                                 // check for collision with synthetic accessors
97                                 if (knownAccessMethods != null) {
98                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
99                                                 if (knownAccessMethods[i] == null)
100                                                         continue;
101                                                 if (this.selector == knownAccessMethods[i].selector
102                                                                 && this.areParametersEqual(methods[i])) {
103                                                         needRename = true;
104                                                         break check;
105                                                 }
106                                         }
107                                 }
108                         }
109                         if (needRename) { // retry with a selector postfixed by a growing
110                                                                 // methodId
111                                 this.selector(CharOperation.concat(AccessMethodPrefix, String
112                                                 .valueOf(++methodId).toCharArray()));
113                         }
114                 } while (needRename);
115
116                 // retrieve sourceStart position for the target field for line number
117                 // attributes
118                 FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
119                 if (fieldDecls != null) {
120                         for (int i = 0, max = fieldDecls.length; i < max; i++) {
121                                 if (fieldDecls[i].binding == targetField) {
122                                         this.sourceStart = fieldDecls[i].sourceStart;
123                                         return;
124                                 }
125                         }
126                 }
127
128                 /*
129                  * did not find the target field declaration - it is a synthetic one
130                  * public class A { public class B { public class C { void foo() {
131                  * System.out.println("A.this = " + A.this); } } } public static void
132                  * main(String args[]) { new A().new B().new C().foo(); } }
133                  */
134                 // We now at this point - per construction - it is for sure an enclosing
135                 // instance, we are going to
136                 // show the target field type declaration location.
137                 this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use
138                                                                                                                                                                         // the
139                                                                                                                                                                         // target
140                                                                                                                                                                         // declaring
141                                                                                                                                                                         // class
142                                                                                                                                                                         // name
143                                                                                                                                                                         // position
144                                                                                                                                                                         // instead
145         }
146
147         public SyntheticAccessMethodBinding(MethodBinding targetMethod,
148                         boolean isSuperAccess, ReferenceBinding receiverType) {
149
150                 if (targetMethod.isConstructor()) {
151                         this.initializeConstructorAccessor(targetMethod);
152                 } else {
153                         this.initializeMethodAccessor(targetMethod, isSuperAccess,
154                                         receiverType);
155                 }
156         }
157
158         /**
159          * An constructor accessor is a constructor with an extra argument
160          * (declaringClass), in case of collision with an existing constructor, then
161          * add again an extra argument (declaringClass again).
162          */
163         public void initializeConstructorAccessor(MethodBinding targetConstructor) {
164
165                 this.targetMethod = targetConstructor;
166                 this.modifiers = AccDefault;// | AccSynthetic;
167                 SourceTypeBinding sourceType = (SourceTypeBinding) targetConstructor.declaringClass;
168                 SyntheticAccessMethodBinding[] knownAccessMethods = sourceType
169                                 .syntheticAccessMethods();
170                 this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
171
172                 this.selector = targetConstructor.selector;
173                 this.returnType = targetConstructor.returnType;
174                 this.accessType = ConstructorAccess;
175                 this.parameters = new TypeBinding[targetConstructor.parameters.length + 1];
176                 System.arraycopy(targetConstructor.parameters, 0, this.parameters, 0,
177                                 targetConstructor.parameters.length);
178                 parameters[targetConstructor.parameters.length] = targetConstructor.declaringClass;
179                 this.thrownExceptions = targetConstructor.thrownExceptions;
180                 this.declaringClass = sourceType;
181
182                 // check for method collision
183                 boolean needRename;
184                 do {
185                         check: {
186                                 needRename = false;
187                                 // check for collision with known methods
188                                 MethodBinding[] methods = sourceType.methods;
189                                 for (int i = 0, length = methods.length; i < length; i++) {
190                                         if (this.selector == methods[i].selector
191                                                         && this.areParametersEqual(methods[i])) {
192                                                 needRename = true;
193                                                 break check;
194                                         }
195                                 }
196                                 // check for collision with synthetic accessors
197                                 if (knownAccessMethods != null) {
198                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
199                                                 if (knownAccessMethods[i] == null)
200                                                         continue;
201                                                 if (this.selector == knownAccessMethods[i].selector
202                                                                 && this
203                                                                                 .areParametersEqual(knownAccessMethods[i])) {
204                                                         needRename = true;
205                                                         break check;
206                                                 }
207                                         }
208                                 }
209                         }
210                         if (needRename) { // retry with a new extra argument
211                                 int length = this.parameters.length;
212                                 System.arraycopy(this.parameters, 0,
213                                                 this.parameters = new TypeBinding[length + 1], 0,
214                                                 length);
215                                 this.parameters[length] = this.declaringClass;
216                         }
217                 } while (needRename);
218
219                 // retrieve sourceStart position for the target method for line number
220                 // attributes
221                 AbstractMethodDeclaration[] methodDecls = sourceType.scope.referenceContext.methods;
222                 if (methodDecls != null) {
223                         for (int i = 0, length = methodDecls.length; i < length; i++) {
224                                 if (methodDecls[i].binding == targetConstructor) {
225                                         this.sourceStart = methodDecls[i].sourceStart;
226                                         return;
227                                 }
228                         }
229                 }
230         }
231
232         /**
233          * An method accessor is a method with an access$N selector, where N is
234          * incremented in case of collisions.
235          */
236         public void initializeMethodAccessor(MethodBinding targetMethod,
237                         boolean isSuperAccess, ReferenceBinding declaringClass) {
238
239                 this.targetMethod = targetMethod;
240                 this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
241                 SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
242                 SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType
243                                 .syntheticAccessMethods();
244                 int methodId = knownAccessMethods == null ? 0
245                                 : knownAccessMethods.length;
246                 this.index = methodId;
247
248                 this.selector = CharOperation.concat(AccessMethodPrefix, String
249                                 .valueOf(methodId).toCharArray());
250                 this.returnType = targetMethod.returnType;
251                 this.accessType = isSuperAccess ? SuperMethodAccess : MethodAccess;
252
253                 if (targetMethod.isStatic()) {
254                         this.parameters = targetMethod.parameters;
255                 } else {
256                         this.parameters = new TypeBinding[targetMethod.parameters.length + 1];
257                         this.parameters[0] = declaringSourceType;
258                         System.arraycopy(targetMethod.parameters, 0, this.parameters, 1,
259                                         targetMethod.parameters.length);
260                 }
261                 this.thrownExceptions = targetMethod.thrownExceptions;
262                 this.declaringClass = declaringSourceType;
263
264                 // check for method collision
265                 boolean needRename;
266                 do {
267                         check: {
268                                 needRename = false;
269                                 // check for collision with known methods
270                                 MethodBinding[] methods = declaringSourceType.methods;
271                                 for (int i = 0, length = methods.length; i < length; i++) {
272                                         if (this.selector == methods[i].selector
273                                                         && this.areParametersEqual(methods[i])) {
274                                                 needRename = true;
275                                                 break check;
276                                         }
277                                 }
278                                 // check for collision with synthetic accessors
279                                 if (knownAccessMethods != null) {
280                                         for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
281                                                 if (knownAccessMethods[i] == null)
282                                                         continue;
283                                                 if (this.selector == knownAccessMethods[i].selector
284                                                                 && this
285                                                                                 .areParametersEqual(knownAccessMethods[i])) {
286                                                         needRename = true;
287                                                         break check;
288                                                 }
289                                         }
290                                 }
291                         }
292                         if (needRename) { // retry with a selector & a growing methodId
293                                 this.selector(CharOperation.concat(AccessMethodPrefix, String
294                                                 .valueOf(++methodId).toCharArray()));
295                         }
296                 } while (needRename);
297
298                 // retrieve sourceStart position for the target method for line number
299                 // attributes
300                 AbstractMethodDeclaration[] methodDecls = declaringSourceType.scope.referenceContext.methods;
301                 if (methodDecls != null) {
302                         for (int i = 0, length = methodDecls.length; i < length; i++) {
303                                 if (methodDecls[i].binding == targetMethod) {
304                                         this.sourceStart = methodDecls[i].sourceStart;
305                                         return;
306                                 }
307                         }
308                 }
309         }
310
311         protected boolean isConstructorRelated() {
312                 return accessType == ConstructorAccess;
313         }
314 }