/******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; public class SyntheticMethodBinding extends MethodBinding { public FieldBinding targetReadField; // read access to a field public FieldBinding targetWriteField; // write access to a field public MethodBinding targetMethod; // method or constructor public int kind; public final static int FieldReadAccess = 1; // field read public final static int FieldWriteAccess = 2; // field write public final static int MethodAccess = 3; // normal method public final static int ConstructorAccess = 4; // constructor public final static int SuperMethodAccess = 5; // super method public final static int BridgeMethod = 6; // bridge method public final static int EnumValues = 7; // enum #values() public final static int EnumValueOf = 8; // enum #valueOf(String) public int sourceStart = 0; // start position of the matching declaration public int index; // used for sorting access methods in the class file public SyntheticMethodBinding(FieldBinding targetField, boolean isReadAccess, ReferenceBinding declaringClass) { this.modifiers = AccDefault | AccStatic | AccSynthetic; SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass; SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods(); int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; this.index = methodId; this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(methodId).toCharArray()); if (isReadAccess) { this.returnType = targetField.type; if (targetField.isStatic()) { this.parameters = NoParameters; } else { this.parameters = new TypeBinding[1]; this.parameters[0] = declaringSourceType; } this.targetReadField = targetField; this.kind = FieldReadAccess; } else { this.returnType = VoidBinding; if (targetField.isStatic()) { this.parameters = new TypeBinding[1]; this.parameters[0] = targetField.type; } else { this.parameters = new TypeBinding[2]; this.parameters[0] = declaringSourceType; this.parameters[1] = targetField.type; } this.targetWriteField = targetField; this.kind = FieldWriteAccess; } this.thrownExceptions = NoExceptions; this.declaringClass = declaringSourceType; // check for method collision boolean needRename; do { check : { needRename = false; // check for collision with known methods MethodBinding[] methods = declaringSourceType.methods; for (int i = 0, length = methods.length; i < length; i++) { if (CharOperation.equals(this.selector, methods[i].selector) && this.areParametersEqual(methods[i])) { needRename = true; break check; } } // check for collision with synthetic accessors if (knownAccessMethods != null) { for (int i = 0, length = knownAccessMethods.length; i < length; i++) { if (knownAccessMethods[i] == null) continue; if (CharOperation.equals(this.selector, knownAccessMethods[i].selector) && this.areParametersEqual(methods[i])) { needRename = true; break check; } } } } if (needRename) { // retry with a selector postfixed by a growing methodId this.setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(++methodId).toCharArray())); } } while (needRename); // retrieve sourceStart position for the target field for line number attributes FieldDeclaration[] fieldDecls = declaringSourceType.scope.referenceContext.fields; if (fieldDecls != null) { for (int i = 0, max = fieldDecls.length; i < max; i++) { if (fieldDecls[i].binding == targetField) { this.sourceStart = fieldDecls[i].sourceStart; return; } } } /* did not find the target field declaration - it is a synthetic one public class A { public class B { public class C { void foo() { System.out.println("A.this = " + A.this); } } } public static void main(String args[]) { new A().new B().new C().foo(); } } */ // We now at this point - per construction - it is for sure an enclosing instance, we are going to // show the target field type declaration location. this.sourceStart = declaringSourceType.scope.referenceContext.sourceStart; // use the target declaring class name position instead } public SyntheticMethodBinding(MethodBinding targetMethod, boolean isSuperAccess, ReferenceBinding receiverType) { if (targetMethod.isConstructor()) { this.initializeConstructorAccessor(targetMethod); } else { this.initializeMethodAccessor(targetMethod, isSuperAccess, receiverType); } } /** * Construct a bridge method */ public SyntheticMethodBinding(MethodBinding overridenMethodToBridge, MethodBinding localTargetMethod) { this.declaringClass = localTargetMethod.declaringClass; this.selector = overridenMethodToBridge.selector; this.modifiers = overridenMethodToBridge.modifiers | AccBridge | AccSynthetic; this.modifiers &= ~(AccAbstract | AccNative); this.returnType = overridenMethodToBridge.returnType; this.parameters = overridenMethodToBridge.parameters; this.thrownExceptions = overridenMethodToBridge.thrownExceptions; this.targetMethod = localTargetMethod; this.kind = BridgeMethod; SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding)this.declaringClass).syntheticMethods(); int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; this.index = methodId; } /** * Construct enum special methods: values or valueOf methods */ public SyntheticMethodBinding(SourceTypeBinding declaringEnum, char[] selector) { if (selector == TypeConstants.VALUES) { this.declaringClass = declaringEnum; this.selector = selector; this.modifiers = AccFinal | AccPublic | AccStatic; this.returnType = declaringEnum.scope.createArrayType(declaringEnum, 1); this.parameters = NoParameters; this.thrownExceptions = NoExceptions; this.kind = EnumValues; SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding)this.declaringClass).syntheticMethods(); int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; this.index = methodId; } else if (selector == TypeConstants.VALUEOF) { this.declaringClass = declaringEnum; this.selector = selector; this.modifiers = AccFinal | AccPublic | AccStatic; this.returnType = declaringEnum; this.parameters = new TypeBinding[]{ declaringEnum.scope.getJavaLangString() }; this.thrownExceptions = NoExceptions; this.kind = EnumValueOf; SyntheticMethodBinding[] knownAccessMethods = ((SourceTypeBinding)this.declaringClass).syntheticMethods(); int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; this.index = methodId; } } /** * An constructor accessor is a constructor with an extra argument (declaringClass), in case of * collision with an existing constructor, then add again an extra argument (declaringClass again). */ public void initializeConstructorAccessor(MethodBinding accessedConstructor) { this.targetMethod = accessedConstructor; this.modifiers = AccDefault | AccSynthetic; SourceTypeBinding sourceType = (SourceTypeBinding) accessedConstructor.declaringClass; SyntheticMethodBinding[] knownSyntheticMethods = sourceType.syntheticMethods(); this.index = knownSyntheticMethods == null ? 0 : knownSyntheticMethods.length; this.selector = accessedConstructor.selector; this.returnType = accessedConstructor.returnType; this.kind = ConstructorAccess; this.parameters = new TypeBinding[accessedConstructor.parameters.length + 1]; System.arraycopy( accessedConstructor.parameters, 0, this.parameters, 0, accessedConstructor.parameters.length); parameters[accessedConstructor.parameters.length] = accessedConstructor.declaringClass; this.thrownExceptions = accessedConstructor.thrownExceptions; this.declaringClass = sourceType; // check for method collision boolean needRename; do { check : { needRename = false; // check for collision with known methods MethodBinding[] methods = sourceType.methods; for (int i = 0, length = methods.length; i < length; i++) { if (CharOperation.equals(this.selector, methods[i].selector) && this.areParametersEqual(methods[i])) { needRename = true; break check; } } // check for collision with synthetic accessors if (knownSyntheticMethods != null) { for (int i = 0, length = knownSyntheticMethods.length; i < length; i++) { if (knownSyntheticMethods[i] == null) continue; if (CharOperation.equals(this.selector, knownSyntheticMethods[i].selector) && this.areParametersEqual(knownSyntheticMethods[i])) { needRename = true; break check; } } } } if (needRename) { // retry with a new extra argument int length = this.parameters.length; System.arraycopy( this.parameters, 0, this.parameters = new TypeBinding[length + 1], 0, length); this.parameters[length] = this.declaringClass; } } while (needRename); // retrieve sourceStart position for the target method for line number attributes AbstractMethodDeclaration[] methodDecls = sourceType.scope.referenceContext.methods; if (methodDecls != null) { for (int i = 0, length = methodDecls.length; i < length; i++) { if (methodDecls[i].binding == accessedConstructor) { this.sourceStart = methodDecls[i].sourceStart; return; } } } } /** * An method accessor is a method with an access$N selector, where N is incremented in case of collisions. */ public void initializeMethodAccessor(MethodBinding accessedMethod, boolean isSuperAccess, ReferenceBinding receiverType) { this.targetMethod = accessedMethod; this.modifiers = AccDefault | AccStatic | AccSynthetic; SourceTypeBinding declaringSourceType = (SourceTypeBinding) receiverType; SyntheticMethodBinding[] knownAccessMethods = declaringSourceType.syntheticMethods(); int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length; this.index = methodId; this.selector = CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(methodId).toCharArray()); this.returnType = accessedMethod.returnType; this.kind = isSuperAccess ? SuperMethodAccess : MethodAccess; if (accessedMethod.isStatic()) { this.parameters = accessedMethod.parameters; } else { this.parameters = new TypeBinding[accessedMethod.parameters.length + 1]; this.parameters[0] = declaringSourceType; System.arraycopy(accessedMethod.parameters, 0, this.parameters, 1, accessedMethod.parameters.length); } this.thrownExceptions = accessedMethod.thrownExceptions; this.declaringClass = declaringSourceType; // check for method collision boolean needRename; do { check : { needRename = false; // check for collision with known methods MethodBinding[] methods = declaringSourceType.methods; for (int i = 0, length = methods.length; i < length; i++) { if (CharOperation.equals(this.selector, methods[i].selector) && this.areParametersEqual(methods[i])) { needRename = true; break check; } } // check for collision with synthetic accessors if (knownAccessMethods != null) { for (int i = 0, length = knownAccessMethods.length; i < length; i++) { if (knownAccessMethods[i] == null) continue; if (CharOperation.equals(this.selector, knownAccessMethods[i].selector) && this.areParametersEqual(knownAccessMethods[i])) { needRename = true; break check; } } } } if (needRename) { // retry with a selector & a growing methodId this.setSelector(CharOperation.concat(TypeConstants.SYNTHETIC_ACCESS_METHOD_PREFIX, String.valueOf(++methodId).toCharArray())); } } while (needRename); // retrieve sourceStart position for the target method for line number attributes AbstractMethodDeclaration[] methodDecls = declaringSourceType.scope.referenceContext.methods; if (methodDecls != null) { for (int i = 0, length = methodDecls.length; i < length; i++) { if (methodDecls[i].binding == accessedMethod) { this.sourceStart = methodDecls[i].sourceStart; return; } } } } protected boolean isConstructorRelated() { return kind == ConstructorAccess; } }