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