001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.lookup;
011:
012: import org.eclipse.jdt.core.compiler.CharOperation;
013: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
014: import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
015: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
016:
017: public class SyntheticMethodBinding extends MethodBinding {
018:
019: public FieldBinding targetReadField; // read access to a field
020: public FieldBinding targetWriteField; // write access to a field
021: public MethodBinding targetMethod; // method or constructor
022: public TypeBinding targetEnumType; // enum type
023:
024: public int kind;
025:
026: public final static int FieldReadAccess = 1; // field read
027: public final static int FieldWriteAccess = 2; // field write
028: public final static int MethodAccess = 3; // normal method
029: public final static int ConstructorAccess = 4; // constructor
030: public final static int SuperMethodAccess = 5; // super method
031: public final static int BridgeMethod = 6; // bridge method
032: public final static int EnumValues = 7; // enum #values()
033: public final static int EnumValueOf = 8; // enum #valueOf(String)
034: public final static int SwitchTable = 9; // switch table method
035:
036: public int sourceStart = 0; // start position of the matching declaration
037: public int index; // used for sorting access methods in the class file
038:
039: public SyntheticMethodBinding(FieldBinding targetField,
040: boolean isReadAccess, ReferenceBinding declaringClass) {
041:
042: this .modifiers = ClassFileConstants.AccDefault
043: | ClassFileConstants.AccStatic
044: | ClassFileConstants.AccSynthetic;
045: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
046: SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
047: SyntheticMethodBinding[] knownAccessMethods = declaringSourceType
048: .syntheticMethods();
049: int methodId = knownAccessMethods == null ? 0
050: : knownAccessMethods.length;
051: this .index = methodId;
052: this .selector = CharOperation.concat(
053: TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String
054: .valueOf(methodId).toCharArray());
055: if (isReadAccess) {
056: this .returnType = targetField.type;
057: if (targetField.isStatic()) {
058: this .parameters = Binding.NO_PARAMETERS;
059: } else {
060: this .parameters = new TypeBinding[1];
061: this .parameters[0] = declaringSourceType;
062: }
063: this .targetReadField = targetField;
064: this .kind = FieldReadAccess;
065: } else {
066: this .returnType = TypeBinding.VOID;
067: if (targetField.isStatic()) {
068: this .parameters = new TypeBinding[1];
069: this .parameters[0] = targetField.type;
070: } else {
071: this .parameters = new TypeBinding[2];
072: this .parameters[0] = declaringSourceType;
073: this .parameters[1] = targetField.type;
074: }
075: this .targetWriteField = targetField;
076: this .kind = FieldWriteAccess;
077: }
078: this .thrownExceptions = Binding.NO_EXCEPTIONS;
079: this .declaringClass = declaringSourceType;
080:
081: // check for method collision
082: boolean needRename;
083: do {
084: check: {
085: needRename = false;
086: // check for collision with known methods
087: long range;
088: MethodBinding[] methods = declaringSourceType.methods();
089: if ((range = ReferenceBinding.binarySearch(
090: this .selector, methods)) >= 0) {
091: int paramCount = this .parameters.length;
092: nextMethod: for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
093: MethodBinding method = methods[imethod];
094: if (method.parameters.length == paramCount) {
095: TypeBinding[] toMatch = method.parameters;
096: for (int i = 0; i < paramCount; i++) {
097: if (toMatch[i] != this .parameters[i]) {
098: continue nextMethod;
099: }
100: }
101: needRename = true;
102: break check;
103: }
104: }
105: }
106: // check for collision with synthetic accessors
107: if (knownAccessMethods != null) {
108: for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
109: if (knownAccessMethods[i] == null)
110: continue;
111: if (CharOperation.equals(this .selector,
112: knownAccessMethods[i].selector)
113: && this .areParametersEqual(methods[i])) {
114: needRename = true;
115: break check;
116: }
117: }
118: }
119: }
120: if (needRename) { // retry with a selector postfixed by a growing methodId
121: this .setSelector(CharOperation.concat(
122: TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
123: String.valueOf(++methodId).toCharArray()));
124: }
125: } while (needRename);
126:
127: // retrieve sourceStart position for the target field for line number attributes
128: FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields;
129: if (fieldDecls != null) {
130: for (int i = 0, max = fieldDecls.length; i < max; i++) {
131: if (fieldDecls[i].binding == targetField) {
132: this .sourceStart = fieldDecls[i].sourceStart;
133: return;
134: }
135: }
136: }
137:
138: /* did not find the target field declaration - it is a synthetic one
139: public class A {
140: public class B {
141: public class C {
142: void foo() {
143: System.out.println("A.this = " + A.this);
144: }
145: }
146: }
147: public static void main(String args[]) {
148: new A().new B().new C().foo();
149: }
150: }
151: */
152: // We now at this point - per construction - it is for sure an enclosing instance, we are going to
153: // show the target field type declaration location.
154: this .sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead
155: }
156:
157: public SyntheticMethodBinding(FieldBinding targetField,
158: ReferenceBinding declaringClass, TypeBinding enumBinding,
159: char[] selector) {
160: this .modifiers = ClassFileConstants.AccDefault
161: | ClassFileConstants.AccStatic
162: | ClassFileConstants.AccSynthetic;
163: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
164: SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
165: SyntheticMethodBinding[] knownAccessMethods = declaringSourceType
166: .syntheticMethods();
167: int methodId = knownAccessMethods == null ? 0
168: : knownAccessMethods.length;
169: this .index = methodId;
170: this .selector = selector;
171: this .returnType = declaringSourceType.scope.createArrayType(
172: TypeBinding.INT, 1);
173: this .parameters = Binding.NO_PARAMETERS;
174: this .targetReadField = targetField;
175: this .targetEnumType = enumBinding;
176: this .kind = SwitchTable;
177: this .thrownExceptions = Binding.NO_EXCEPTIONS;
178: this .declaringClass = declaringSourceType;
179:
180: if (declaringSourceType.isStrictfp()) {
181: this .modifiers |= ClassFileConstants.AccStrictfp;
182: }
183: // check for method collision
184: boolean needRename;
185: do {
186: check: {
187: needRename = false;
188: // check for collision with known methods
189: long range;
190: MethodBinding[] methods = declaringSourceType.methods();
191: if ((range = ReferenceBinding.binarySearch(
192: this .selector, methods)) >= 0) {
193: int paramCount = this .parameters.length;
194: nextMethod: for (int imethod = (int) range, end = (int) (range >> 32); imethod <= end; imethod++) {
195: MethodBinding method = methods[imethod];
196: if (method.parameters.length == paramCount) {
197: TypeBinding[] toMatch = method.parameters;
198: for (int i = 0; i < paramCount; i++) {
199: if (toMatch[i] != this .parameters[i]) {
200: continue nextMethod;
201: }
202: }
203: needRename = true;
204: break check;
205: }
206: }
207: }
208: // check for collision with synthetic accessors
209: if (knownAccessMethods != null) {
210: for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
211: if (knownAccessMethods[i] == null)
212: continue;
213: if (CharOperation.equals(this .selector,
214: knownAccessMethods[i].selector)
215: && this .areParametersEqual(methods[i])) {
216: needRename = true;
217: break check;
218: }
219: }
220: }
221: }
222: if (needRename) { // retry with a selector postfixed by a growing methodId
223: this .setSelector(CharOperation.concat(selector, String
224: .valueOf(++methodId).toCharArray()));
225: }
226: } while (needRename);
227:
228: // We now at this point - per construction - it is for sure an enclosing instance, we are going to
229: // show the target field type declaration location.
230: this .sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead
231: }
232:
233: public SyntheticMethodBinding(MethodBinding targetMethod,
234: boolean isSuperAccess, ReferenceBinding receiverType) {
235:
236: if (targetMethod.isConstructor()) {
237: this .initializeConstructorAccessor(targetMethod);
238: } else {
239: this .initializeMethodAccessor(targetMethod, isSuperAccess,
240: receiverType);
241: }
242: }
243:
244: /**
245: * Construct a bridge method
246: */
247: public SyntheticMethodBinding(
248: MethodBinding overridenMethodToBridge,
249: MethodBinding targetMethod, SourceTypeBinding declaringClass) {
250:
251: this .declaringClass = declaringClass;
252: this .selector = overridenMethodToBridge.selector;
253: // amongst other, clear the AccGenericSignature, so as to ensure no remains of original inherited persist (101794)
254: // also use the modifiers from the target method, as opposed to inherited one (147690)
255: this .modifiers = (targetMethod.modifiers
256: | ClassFileConstants.AccBridge | ClassFileConstants.AccSynthetic)
257: & ~(ClassFileConstants.AccAbstract
258: | ClassFileConstants.AccNative
259: | ClassFileConstants.AccFinal | ExtraCompilerModifiers.AccGenericSignature);
260: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
261: this .returnType = overridenMethodToBridge.returnType;
262: this .parameters = overridenMethodToBridge.parameters;
263: this .thrownExceptions = overridenMethodToBridge.thrownExceptions;
264: this .targetMethod = targetMethod;
265: this .kind = BridgeMethod;
266: SyntheticMethodBinding[] knownAccessMethods = declaringClass
267: .syntheticMethods();
268: int methodId = knownAccessMethods == null ? 0
269: : knownAccessMethods.length;
270: this .index = methodId;
271: }
272:
273: /**
274: * Construct enum special methods: values or valueOf methods
275: */
276: public SyntheticMethodBinding(SourceTypeBinding declaringEnum,
277: char[] selector) {
278: this .declaringClass = declaringEnum;
279: this .selector = selector;
280: this .modifiers = ClassFileConstants.AccPublic
281: | ClassFileConstants.AccStatic;
282: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
283: this .thrownExceptions = Binding.NO_EXCEPTIONS;
284: if (selector == TypeConstants.VALUES) {
285: this .returnType = declaringEnum.scope.createArrayType(
286: declaringEnum, 1);
287: this .parameters = Binding.NO_PARAMETERS;
288: this .kind = EnumValues;
289: } else if (selector == TypeConstants.VALUEOF) {
290: this .returnType = declaringEnum;
291: this .parameters = new TypeBinding[] { declaringEnum.scope
292: .getJavaLangString() };
293: this .kind = EnumValueOf;
294: }
295: SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding) this .declaringClass)
296: .syntheticMethods();
297: int methodId = knownAccessMethods == null ? 0
298: : knownAccessMethods.length;
299: this .index = methodId;
300: if (declaringEnum.isStrictfp()) {
301: this .modifiers |= ClassFileConstants.AccStrictfp;
302: }
303: }
304:
305: /**
306: * An constructor accessor is a constructor with an extra argument (declaringClass), in case of
307: * collision with an existing constructor, then add again an extra argument (declaringClass again).
308: */
309: public void initializeConstructorAccessor(
310: MethodBinding accessedConstructor) {
311:
312: this .targetMethod = accessedConstructor;
313: this .modifiers = ClassFileConstants.AccDefault
314: | ClassFileConstants.AccSynthetic;
315: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
316: SourceTypeBinding sourceType = (SourceTypeBinding) accessedConstructor.declaringClass;
317: SyntheticMethodBinding[] knownSyntheticMethods = sourceType
318: .syntheticMethods();
319: this .index = knownSyntheticMethods == null ? 0
320: : knownSyntheticMethods.length;
321:
322: this .selector = accessedConstructor.selector;
323: this .returnType = accessedConstructor.returnType;
324: this .kind = ConstructorAccess;
325: this .parameters = new TypeBinding[accessedConstructor.parameters.length + 1];
326: System.arraycopy(accessedConstructor.parameters, 0,
327: this .parameters, 0,
328: accessedConstructor.parameters.length);
329: parameters[accessedConstructor.parameters.length] = accessedConstructor.declaringClass;
330: this .thrownExceptions = accessedConstructor.thrownExceptions;
331: this .declaringClass = sourceType;
332:
333: // check for method collision
334: boolean needRename;
335: do {
336: check: {
337: needRename = false;
338: // check for collision with known methods
339: MethodBinding[] methods = sourceType.methods();
340: for (int i = 0, length = methods.length; i < length; i++) {
341: if (CharOperation.equals(this .selector,
342: methods[i].selector)
343: && this .areParametersEqual(methods[i])) {
344: needRename = true;
345: break check;
346: }
347: }
348: // check for collision with synthetic accessors
349: if (knownSyntheticMethods != null) {
350: for (int i = 0, length = knownSyntheticMethods.length; i < length; i++) {
351: if (knownSyntheticMethods[i] == null)
352: continue;
353: if (CharOperation.equals(this .selector,
354: knownSyntheticMethods[i].selector)
355: && this
356: .areParametersEqual(knownSyntheticMethods[i])) {
357: needRename = true;
358: break check;
359: }
360: }
361: }
362: }
363: if (needRename) { // retry with a new extra argument
364: int length = this .parameters.length;
365: System.arraycopy(this .parameters, 0,
366: this .parameters = new TypeBinding[length + 1],
367: 0, length);
368: this .parameters[length] = this .declaringClass;
369: }
370: } while (needRename);
371:
372: // retrieve sourceStart position for the target method for line number attributes
373: AbstractMethodDeclaration[] methodDecls = sourceType.scope.referenceContext.methods;
374: if (methodDecls != null) {
375: for (int i = 0, length = methodDecls.length; i < length; i++) {
376: if (methodDecls[i].binding == accessedConstructor) {
377: this .sourceStart = methodDecls[i].sourceStart;
378: return;
379: }
380: }
381: }
382: }
383:
384: /**
385: * An method accessor is a method with an access$N selector, where N is incremented in case of collisions.
386: */
387: public void initializeMethodAccessor(MethodBinding accessedMethod,
388: boolean isSuperAccess, ReferenceBinding receiverType) {
389:
390: this .targetMethod = accessedMethod;
391: this .modifiers = ClassFileConstants.AccDefault
392: | ClassFileConstants.AccStatic
393: | ClassFileConstants.AccSynthetic;
394: this .tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved);
395: SourceTypeBinding declaringSourceType = (SourceTypeBinding) receiverType;
396: SyntheticMethodBinding[] knownAccessMethods = declaringSourceType
397: .syntheticMethods();
398: int methodId = knownAccessMethods == null ? 0
399: : knownAccessMethods.length;
400: this .index = methodId;
401:
402: this .selector = CharOperation.concat(
403: TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String
404: .valueOf(methodId).toCharArray());
405: this .returnType = accessedMethod.returnType;
406: this .kind = isSuperAccess ? SuperMethodAccess : MethodAccess;
407:
408: if (accessedMethod.isStatic()) {
409: this .parameters = accessedMethod.parameters;
410: } else {
411: this .parameters = new TypeBinding[accessedMethod.parameters.length + 1];
412: this .parameters[0] = declaringSourceType;
413: System.arraycopy(accessedMethod.parameters, 0,
414: this .parameters, 1,
415: accessedMethod.parameters.length);
416: }
417: this .thrownExceptions = accessedMethod.thrownExceptions;
418: this .declaringClass = declaringSourceType;
419:
420: // check for method collision
421: boolean needRename;
422: do {
423: check: {
424: needRename = false;
425: // check for collision with known methods
426: MethodBinding[] methods = declaringSourceType.methods();
427: for (int i = 0, length = methods.length; i < length; i++) {
428: if (CharOperation.equals(this .selector,
429: methods[i].selector)
430: && this .areParametersEqual(methods[i])) {
431: needRename = true;
432: break check;
433: }
434: }
435: // check for collision with synthetic accessors
436: if (knownAccessMethods != null) {
437: for (int i = 0, length = knownAccessMethods.length; i < length; i++) {
438: if (knownAccessMethods[i] == null)
439: continue;
440: if (CharOperation.equals(this .selector,
441: knownAccessMethods[i].selector)
442: && this
443: .areParametersEqual(knownAccessMethods[i])) {
444: needRename = true;
445: break check;
446: }
447: }
448: }
449: }
450: if (needRename) { // retry with a selector & a growing methodId
451: this .setSelector(CharOperation.concat(
452: TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX,
453: String.valueOf(++methodId).toCharArray()));
454: }
455: } while (needRename);
456:
457: // retrieve sourceStart position for the target method for line number attributes
458: AbstractMethodDeclaration[] methodDecls = declaringSourceType.scope.referenceContext.methods;
459: if (methodDecls != null) {
460: for (int i = 0, length = methodDecls.length; i < length; i++) {
461: if (methodDecls[i].binding == accessedMethod) {
462: this .sourceStart = methodDecls[i].sourceStart;
463: return;
464: }
465: }
466: }
467: }
468:
469: protected boolean isConstructorRelated() {
470: return kind == ConstructorAccess;
471: }
472: }
|