8272564: Incorrect attribution of method invocations of Object methods on interfaces

Reviewed-by: jlaskey, mcimadamore, vromero
This commit is contained in:
Jan Lahoda 2021-10-05 10:17:24 +00:00
parent a914ee7216
commit a5080effc7
6 changed files with 180 additions and 4 deletions

View File

@ -2590,7 +2590,7 @@ public class Attr extends JCTree.Visitor {
//where
Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, List<Type> argtypes, Type restype) {
if (msym != null &&
msym.owner == syms.objectType.tsym &&
(msym.owner == syms.objectType.tsym || msym.owner.isInterface()) &&
methodName == names.getClass &&
argtypes.isEmpty()) {
// as a special case, x.getClass() has type Class<? extends |X|>

View File

@ -469,7 +469,7 @@ public class Resolve {
return true;
else {
Symbol s2 = ((MethodSymbol)sym).implementation(site.tsym, types, true);
return (s2 == null || s2 == sym || sym.owner == s2.owner ||
return (s2 == null || s2 == sym || sym.owner == s2.owner || (sym.owner.isInterface() && s2.owner == syms.objectType.tsym) ||
!types.isSubSignature(types.memberType(site, s2), types.memberType(site, sym)));
}
}
@ -1854,7 +1854,8 @@ public class Resolve {
List<Type>[] itypes = (List<Type>[])new List[] { List.<Type>nil(), List.<Type>nil() };
InterfaceLookupPhase iphase = InterfaceLookupPhase.ABSTRACT_OK;
for (TypeSymbol s : superclasses(intype)) {
boolean isInterface = site.tsym.isInterface();
for (TypeSymbol s : isInterface ? List.of(intype.tsym) : superclasses(intype)) {
bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes,
s.members(), bestSoFar, allowBoxing, useVarargs, true);
if (name == names.init) return bestSoFar;
@ -1892,6 +1893,19 @@ public class Resolve {
}
}
}
if (isInterface && bestSoFar.kind.isResolutionError()) {
bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes,
syms.objectType.tsym.members(), bestSoFar, allowBoxing, useVarargs, true);
if (bestSoFar.kind.isValid()) {
Symbol baseSymbol = bestSoFar;
bestSoFar = new MethodSymbol(bestSoFar.flags_field, bestSoFar.name, bestSoFar.type, intype.tsym) {
@Override
public Symbol baseSymbol() {
return baseSymbol;
}
};
}
}
return bestSoFar;
}

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8012929 8243074 8266281
* @bug 8012929 8243074 8266281 8272564
* @summary Trees.getElement should work not only for declaration trees, but also for use-trees
* @modules jdk.compiler
* @build TestGetElementReference

View File

@ -37,6 +37,10 @@ public class TestGetElementReferenceData {
target(TestGetElementReferenceData :: test/*getElement:METHOD:test.nested.TestGetElementReferenceData.test()*/);
Object/*getElement:CLASS:java.lang.Object*/ o = null;
if (o/*getElement:LOCAL_VARIABLE:o*/ instanceof String/*getElement:CLASS:java.lang.String*/ str/*getElement:BINDING_VARIABLE:str*/) ;
I i = null;
i.toString/*getElement:METHOD:test.nested.TestGetElementReferenceData.I.toString()*/();
J j = null;
j.toString/*getElement:METHOD:test.nested.TestGetElementReferenceData.I.toString()*/();
}
private static void target(Runnable r) { r.run(); }
public static class Base {
@ -50,4 +54,8 @@ public class TestGetElementReferenceData {
@Target(ElementType.TYPE_USE)
@interface TypeAnnotation {
}
interface I {
public String toString();
}
interface J extends I {}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8272564
* @summary Verify accessibility of Object-based methods inherited from super interfaces.
* @modules jdk.compiler
*/
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.Scope;
import com.sun.source.util.JavacTask;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
import java.net.URI;
import java.util.Arrays;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
public class TestIsAccessible {
public static void main(String[] args) throws Exception {
final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
assert tool != null;
final JavacTask ct = (JavacTask)tool.getTask(null, null, null, null, null, Arrays.asList(new MyFileObject()));
CompilationUnitTree cut = ct.parse().iterator().next();
TreePath tp = new TreePath(new TreePath(cut), cut.getTypeDecls().get(0));
Scope s = Trees.instance(ct).getScope(tp);
TypeElement name = ct.getElements().getTypeElement("javax.lang.model.element.Name");
Trees trees = Trees.instance(ct);
if (trees.isAccessible(s, name)) {
for (Element member : ct.getElements().getAllMembers(name)) {
if (!trees.isAccessible(s, member, (DeclaredType) name.asType())) {
throw new IllegalStateException("Inaccessible Name member: " + member);
}
}
}
}
static class MyFileObject extends SimpleJavaFileObject {
public MyFileObject() {
super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return "public class Test { }";
}
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8272564
* @summary Correct resolution of toString() (and other similar calls) on interfaces
* @modules jdk.jdeps/com.sun.tools.classfile
* @compile NoObjectToString.java
* @run main NoObjectToString
*/
import java.io.*;
import com.sun.tools.classfile.*;
import com.sun.tools.classfile.ConstantPool.CONSTANT_Methodref_info;
public class NoObjectToString {
public static void main(String... args) throws Exception {
NoObjectToString c = new NoObjectToString();
c.run(args);
}
void run(String... args) throws Exception {
//Verify there are no references to Object.toString() in a Test:
InputStream in = NoObjectToString.class.getResourceAsStream("NoObjectToString$Test.class");
try {
ClassFile cf = ClassFile.read(in);
for (ConstantPool.CPInfo cpinfo: cf.constant_pool.entries()) {
if (cpinfo.getTag() == ConstantPool.CONSTANT_Methodref) {
CONSTANT_Methodref_info ref = (CONSTANT_Methodref_info) cpinfo;
String methodDesc = ref.getClassInfo().getName() + "." + ref.getNameAndTypeInfo().getName() + ":" + ref.getNameAndTypeInfo().getType();
if ("java/lang/Object.toString:()Ljava/lang/String;".equals(methodDesc)) {
throw new AssertionError("Found call to j.l.Object.toString");
}
}
}
} catch (ConstantPoolException ignore) {
throw new AssertionError(ignore);
} finally {
in.close();
}
}
class Test {
void test(I i, J j, K k) {
i.toString();
j.toString();
k.toString();
}
}
interface I {
public String toString();
}
interface J extends I {}
interface K {}
}