new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / lookup / SyntheticAccessMethodBinding.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/lookup/SyntheticAccessMethodBinding.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/compiler/lookup/SyntheticAccessMethodBinding.java
new file mode 100644 (file)
index 0000000..7b729e9
--- /dev/null
@@ -0,0 +1,288 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.lookup;
+
+import net.sourceforge.phpdt.core.compiler.CharOperation;
+import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpeclipse.internal.compiler.ast.FieldDeclaration;
+
+public class SyntheticAccessMethodBinding 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 accessType;
+
+       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
+
+       final static char[] AccessMethodPrefix = { 'a', 'c', 'c', 'e', 's', 's', '$' };
+
+       public int sourceStart = 0; // start position of the matching declaration
+       public int index; // used for sorting access methods in the class file
+       
+       public SyntheticAccessMethodBinding(FieldBinding targetField, boolean isReadAccess, ReferenceBinding declaringClass) {
+
+               this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
+               SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
+               SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType.syntheticAccessMethods();
+               int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+               this.index = methodId;
+               this.selector = CharOperation.concat(AccessMethodPrefix, 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.accessType = 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.accessType = 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 (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 (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.selector(CharOperation.concat(AccessMethodPrefix, 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 SyntheticAccessMethodBinding(MethodBinding targetMethod, boolean isSuperAccess, ReferenceBinding receiverType) {
+       
+               if (targetMethod.isConstructor()) {
+                       this.initializeConstructorAccessor(targetMethod);
+               } else {
+                       this.initializeMethodAccessor(targetMethod, isSuperAccess, receiverType);
+               }
+       }
+
+       /**
+        * 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 targetConstructor) {
+       
+               this.targetMethod = targetConstructor;
+               this.modifiers = AccDefault;// | AccSynthetic;
+               SourceTypeBinding sourceType = (SourceTypeBinding) targetConstructor.declaringClass; 
+               SyntheticAccessMethodBinding[] knownAccessMethods = 
+                       sourceType.syntheticAccessMethods(); 
+               this.index = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+       
+               this.selector = targetConstructor.selector;
+               this.returnType = targetConstructor.returnType;
+               this.accessType = ConstructorAccess;
+               this.parameters = new TypeBinding[targetConstructor.parameters.length + 1];
+               System.arraycopy(
+                       targetConstructor.parameters, 
+                       0, 
+                       this.parameters, 
+                       0, 
+                       targetConstructor.parameters.length); 
+               parameters[targetConstructor.parameters.length] = 
+                       targetConstructor.declaringClass; 
+               this.thrownExceptions = targetConstructor.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 (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 (this.selector == knownAccessMethods[i].selector
+                                                       && this.areParametersEqual(knownAccessMethods[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 == targetConstructor) {
+                                       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 targetMethod, boolean isSuperAccess, ReferenceBinding declaringClass) {
+               
+               this.targetMethod = targetMethod;
+               this.modifiers = AccDefault | AccStatic;// | AccSynthetic;
+               SourceTypeBinding declaringSourceType = (SourceTypeBinding) declaringClass;
+               SyntheticAccessMethodBinding[] knownAccessMethods = declaringSourceType.syntheticAccessMethods();
+               int methodId = knownAccessMethods == null ? 0 : knownAccessMethods.length;
+               this.index = methodId;
+       
+               this.selector = CharOperation.concat(AccessMethodPrefix, String.valueOf(methodId).toCharArray());
+               this.returnType = targetMethod.returnType;
+               this.accessType = isSuperAccess ? SuperMethodAccess : MethodAccess;
+               
+               if (targetMethod.isStatic()) {
+                       this.parameters = targetMethod.parameters;
+               } else {
+                       this.parameters = new TypeBinding[targetMethod.parameters.length + 1];
+                       this.parameters[0] = declaringSourceType;
+                       System.arraycopy(targetMethod.parameters, 0, this.parameters, 1, targetMethod.parameters.length);
+               }
+               this.thrownExceptions = targetMethod.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 (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 (this.selector == knownAccessMethods[i].selector && this.areParametersEqual(knownAccessMethods[i])) {
+                                                       needRename = true;
+                                                       break check;
+                                               }
+                                       }
+                               }
+                       }
+                       if (needRename) { // retry with a selector & a growing methodId
+                               this.selector(CharOperation.concat(AccessMethodPrefix, 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 == targetMethod) {
+                                       this.sourceStart = methodDecls[i].sourceStart;
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       protected boolean isConstructorRelated() {
+               return accessType == ConstructorAccess;
+       }
+}