### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java,v retrieving revision 1.111 diff -u -r1.111 MethodVerifier.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 19 Feb 2010 10:14:13 -0000 1.111 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier.java 23 Mar 2010 11:22:17 -0000 @@ -439,6 +439,14 @@ inherited[i].original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } + if (current == null && this.type.isPublic()) { + int length = inherited.length; + for (int i = 0; i < length; i++) { + MethodBinding inheritedMethod = inherited[i]; + if (inheritedMethod.isPublic() && !inheritedMethod.declaringClass.isPublic()) + this.type.addSyntheticBridgeMethod(inheritedMethod); + } + } if (current == null && skipInheritedMethods) continue nextSelector; @@ -473,6 +481,10 @@ for (int i = 0, length = inherited.length; i < length; i++) { MethodBinding inheritedMethod = inherited[i]; if (inheritedMethod == null) continue; + if (current != null && this.type.isPublic()) { + if (inheritedMethod.isPublic() && !inheritedMethod.declaringClass.isPublic()) + this.type.addSyntheticBridgeMethod(inheritedMethod); + } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, // inherited methods of super classes are too. current == null case handled already. if (!isOrEnclosedByPrivateType && current != null) { Index: compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java,v retrieving revision 1.110 diff -u -r1.110 MethodVerifier15.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 23 Feb 2010 06:00:46 -0000 1.110 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java 23 Mar 2010 11:22:18 -0000 @@ -394,6 +394,14 @@ inherited[i].original().modifiers |= ExtraCompilerModifiers.AccLocallyUsed; } } + if (current == null && this.type.isPublic()) { + int length = inherited.length; + for (int i = 0; i < length; i++) { + MethodBinding inheritedMethod = inherited[i]; + if (inheritedMethod.isPublic() && !inheritedMethod.declaringClass.isPublic()) + this.type.addSyntheticBridgeMethod(inheritedMethod); + } + } if (current == null && skipInheritedMethods) continue nextSelector; @@ -444,6 +452,12 @@ boolean[] skip = new boolean[inheritedLength]; for (int i = 0; i < inheritedLength; i++) { MethodBinding matchMethod = foundMatch[i]; + if (matchMethod == null && current != null && this.type.isPublic()) { // current == null case handled already. + MethodBinding inheritedMethod = inherited[i]; + if (inheritedMethod.isPublic() && !inheritedMethod.declaringClass.isPublic()) { + this.type.addSyntheticBridgeMethod(inheritedMethod); + } + } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296660, if current type is exposed, // inherited methods of super classes are too. current == null case handled already. if (!isOrEnclosedByPrivateType && matchMethod == null && current != null) { Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java,v retrieving revision 1.176 diff -u -r1.176 SourceTypeBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 5 Feb 2010 09:07:01 -0000 1.176 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java 23 Mar 2010 11:22:19 -0000 @@ -537,6 +537,50 @@ } return accessMethod; } +/* + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658. Generate a bridge method if a public method is inherited + * from a non-public class into a public class + */ +public SyntheticMethodBinding addSyntheticBridgeMethod(MethodBinding inheritedMethodToBridge) { + + if (isInterface()) return null; + if (inheritedMethodToBridge.isAbstract() || inheritedMethodToBridge.isFinal() || inheritedMethodToBridge.isStatic()) { + return null; + } + if (this.synthetics == null) + this.synthetics = new HashMap[MAX_SYNTHETICS]; + if (this.synthetics[SourceTypeBinding.METHOD_EMUL] == null) { + this.synthetics[SourceTypeBinding.METHOD_EMUL] = new HashMap(5); + } else { + // check to see if there is another equivalent inheritedMethod already added + Iterator synthMethods = this.synthetics[SourceTypeBinding.METHOD_EMUL].keySet().iterator(); + while (synthMethods.hasNext()) { + Object synthetic = synthMethods.next(); + if (synthetic instanceof MethodBinding) { + MethodBinding method = (MethodBinding) synthetic; + if (CharOperation.equals(inheritedMethodToBridge.selector, method.selector) + && inheritedMethodToBridge.returnType.erasure() == method.returnType.erasure() + && inheritedMethodToBridge.areParameterErasuresEqual(method)) { + return null; + } + } + } + } + + SyntheticMethodBinding accessMethod = null; + SyntheticMethodBinding[] accessors = (SyntheticMethodBinding[]) this.synthetics[SourceTypeBinding.METHOD_EMUL].get(inheritedMethodToBridge); + if (accessors == null) { + accessMethod = new SyntheticMethodBinding(inheritedMethodToBridge, this); + this.synthetics[SourceTypeBinding.METHOD_EMUL].put(inheritedMethodToBridge, accessors = new SyntheticMethodBinding[2]); + accessors[0] = accessMethod; + } else { + if ((accessMethod = accessors[0]) == null) { + accessMethod = new SyntheticMethodBinding(inheritedMethodToBridge, this); + accessors[0] = accessMethod; + } + } + return accessMethod; +} boolean areFieldsInitialized() { return this.fields != Binding.UNINITIALIZED_FIELDS; } Index: compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java,v retrieving revision 1.24 diff -u -r1.24 SyntheticMethodBinding.java --- compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java 17 Sep 2009 17:43:38 -0000 1.24 +++ compiler/org/eclipse/jdt/internal/compiler/lookup/SyntheticMethodBinding.java 23 Mar 2010 11:22:19 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -264,6 +264,27 @@ this.modifiers |= ClassFileConstants.AccStrictfp; } } + + // Create a synthetic method that will simply call the super classes method. + // Used when a public method is inherited from a non-public class into a public class. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658 + public SyntheticMethodBinding(MethodBinding overridenMethodToBridge, SourceTypeBinding declaringClass) { + + this.declaringClass = declaringClass; + this.selector = overridenMethodToBridge.selector; + // amongst other, clear the AccGenericSignature, so as to ensure no remains of original inherited persist (101794) + // also use the modifiers from the target method, as opposed to inherited one (147690) + this.modifiers = (overridenMethodToBridge.modifiers | ClassFileConstants.AccBridge | ClassFileConstants.AccSynthetic) & ~(ClassFileConstants.AccAbstract | ClassFileConstants.AccNative | ClassFileConstants.AccFinal | ExtraCompilerModifiers.AccGenericSignature); + this.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); + this.returnType = overridenMethodToBridge.returnType; + this.parameters = overridenMethodToBridge.parameters; + this.thrownExceptions = overridenMethodToBridge.thrownExceptions; + this.targetMethod = overridenMethodToBridge; + this.purpose = SyntheticMethodBinding.SuperMethodAccess; + SyntheticMethodBinding[] knownAccessMethods = 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 #P org.eclipse.jdt.core.tests.compiler Index: src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java,v retrieving revision 1.198 diff -u -r1.198 MethodVerifyTest.java --- src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 3 Mar 2010 18:18:50 -0000 1.198 +++ src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java 23 Mar 2010 11:22:27 -0000 @@ -10832,4 +10832,79 @@ }, "class java.lang.Object"); } +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658, make sure a bridge method +// is generated when a public method is inherited from a non-public class into a +// public class. +public void test208() { + this.runConformTest( + new String[] { + "Test.java", + "import java.lang.annotation.Annotation;\n"+ + "import java.lang.annotation.Retention;\n"+ + "import java.lang.annotation.RetentionPolicy;\n"+ + "import java.lang.reflect.Method;\n"+ + "\n"+ + "public class Test extends Super {\n"+ + " public static void main(String[] args) {\n"+ + " try {\n"+ + " Method m = Test.class.getMethod(\"setFoo\", String.class);\n"+ + " Annotation a = m.getAnnotation(Anno.class);\n"+ + " System.out.println(\"Annotation was \" + (a == null ? \"not \" : \"\") +\n"+ + "\"found\");\n"+ + " } catch (Exception e) {\n"+ + " e.printStackTrace();\n"+ + " }\n"+ + " }\n"+ + "}\n"+ + "\n"+ + "class Super {\n"+ + " @Anno\n"+ + " public void setFoo(String foo) {}\n"+ + "}\n"+ + "\n"+ + "@Retention(RetentionPolicy.RUNTIME)\n"+ + "@interface Anno {\n"+ + "\n"+ + "}\n" + }, + "Annotation was not found"); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=288658, make sure a bridge method +// is generated when a public method is inherited from a non-public class into a +// public class. +public void test208a() { + this.runConformTest( + new String[] { + "Test.java", + "import java.lang.annotation.Annotation;\n"+ + "import java.lang.annotation.Retention;\n"+ + "import java.lang.annotation.RetentionPolicy;\n"+ + "import java.lang.reflect.Method;\n"+ + "\n"+ + "public class Test extends Super {\n"+ + " public void setFoo() {}\n" + + " public static void main(String[] args) {\n"+ + " try {\n"+ + " Method m = Test.class.getMethod(\"setFoo\", String.class);\n"+ + " Annotation a = m.getAnnotation(Anno.class);\n"+ + " System.out.println(\"Annotation was \" + (a == null ? \"not \" : \"\") +\n"+ + "\"found\");\n"+ + " } catch (Exception e) {\n"+ + " e.printStackTrace();\n"+ + " }\n"+ + " }\n"+ + "}\n"+ + "\n"+ + "class Super {\n"+ + " @Anno\n"+ + " public void setFoo(String foo) {}\n"+ + "}\n"+ + "\n"+ + "@Retention(RetentionPolicy.RUNTIME)\n"+ + "@interface Anno {\n"+ + "\n"+ + "}\n" + }, + "Annotation was not found"); +} }