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