# HG changeset patch # User psandoz # Date 1460394049 -7200 # Node ID 86f49af093e50d7eccdb8c925183cee527c8de35 # Parent 951bb58383a40ff23072050856fa21bdc9b6996a 8152645: VarHandle lookup access control tests Reviewed-by: mhaupt, redestad diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/TestFieldLookupAccessibility.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/TestFieldLookupAccessibility.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2016, 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 8152645 + * @summary test field lookup accessibility of MethodHandles and VarHandles + * @compile TestFieldLookupAccessibility.java + * pkg/A.java pkg/B_extends_A.java pkg/C.java + * pkg/subpkg/B_extends_A.java pkg/subpkg/C.java + * @run testng/othervm TestFieldLookupAccessibility + */ + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import pkg.B_extends_A; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TestFieldLookupAccessibility { + + // The set of possible field lookup mechanisms + enum FieldLookup { + MH_GETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findGetter(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return !Modifier.isStatic(f.getModifiers()); + } + }, + MH_SETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findSetter(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return !Modifier.isStatic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()); + } + }, + MH_STATIC_GETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findStaticGetter(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return Modifier.isStatic(f.getModifiers()); + } + }, + MH_STATIC_SETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findStaticSetter(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return Modifier.isStatic(f.getModifiers()) && !Modifier.isFinal(f.getModifiers()); + } + }, + MH_UNREFLECT_GETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.unreflectGetter(f); + } + }, + MH_UNREFLECT_GETTER_ACCESSIBLE() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.unreflectGetter(cloneAndSetAccessible(f)); + } + }, + MH_UNREFLECT_SETTER() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.unreflectSetter(f); + } + + boolean isAccessible(Field f) { + return f.isAccessible() || !Modifier.isFinal(f.getModifiers()); + } + }, + MH_UNREFLECT_SETTER_ACCESSIBLE() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.unreflectSetter(cloneAndSetAccessible(f)); + } + }, + VH() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findVarHandle(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return !Modifier.isStatic(f.getModifiers()); + } + }, + VH_STATIC() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.findStaticVarHandle(f.getDeclaringClass(), f.getName(), f.getType()); + } + + boolean isAccessible(Field f) { + return Modifier.isStatic(f.getModifiers()); + } + }, + VH_UNREFLECT() { + Object lookup(MethodHandles.Lookup l, Field f) throws Exception { + return l.unreflectVarHandle(f); + } + }; + + // Look up a handle to a field + abstract Object lookup(MethodHandles.Lookup l, Field f) throws Exception; + + boolean isAccessible(Field f) { + return true; + } + + static Field cloneAndSetAccessible(Field f) throws Exception { + // Clone to avoid mutating source field + f = f.getDeclaringClass().getDeclaredField(f.getName()); + f.setAccessible(true); + return f; + } + } + + @DataProvider + public Object[][] lookupProvider() throws Exception { + Stream> baseCases = Stream.of( + // Look up from same package + List.of(pkg.A.class, pkg.A.lookup(), pkg.A.inaccessibleFields()), + List.of(pkg.A.class, pkg.A.lookup(), pkg.A.inaccessibleFields()), + List.of(pkg.A.class, B_extends_A.lookup(), B_extends_A.inaccessibleFields()), + List.of(pkg.A.class, pkg.C.lookup(), pkg.C.inaccessibleFields()), + + // Look up from sub-package + List.of(pkg.A.class, pkg.subpkg.B_extends_A.lookup(), pkg.subpkg.B_extends_A.inaccessibleFields()), + List.of(pkg.A.class, pkg.subpkg.C.lookup(), pkg.subpkg.C.inaccessibleFields()) + ); + + // Cross product base cases with the field lookup classes + return baseCases. + flatMap(l -> Stream.of(FieldLookup.values()).map(fl -> prepend(fl, l))). + toArray(Object[][]::new); + } + + private static Object[] prepend(Object o, List l) { + List pl = new ArrayList<>(); + pl.add(o); + pl.addAll(l); + return pl.toArray(); + } + + @Test(dataProvider = "lookupProvider") + public void test(FieldLookup fl, Class src, MethodHandles.Lookup l, Set inaccessibleFields) { + // Add to the expected failures all inaccessible fields due to accessibility modifiers + Set expected = new HashSet<>(inaccessibleFields); + Map actual = new HashMap<>(); + + for (Field f : fields(src)) { + // Add to the expected failures all inaccessible fields due to static/final modifiers + if (!fl.isAccessible(f)) { + expected.add(f.getName()); + } + + try { + fl.lookup(l, f); + } + catch (Throwable t) { + // Lookup failed, add to the actual failures + actual.put(f, t); + } + } + + Set actualFieldNames = actual.keySet().stream().map(Field::getName). + collect(Collectors.toSet()); + if (!actualFieldNames.equals(expected)) { + if (actualFieldNames.isEmpty()) { + // Setting the accessibility bit of a Field grants access under + // all conditions for MethodHander getters and setters + if (fl != FieldLookup.MH_UNREFLECT_GETTER_ACCESSIBLE && + fl != FieldLookup.MH_UNREFLECT_SETTER_ACCESSIBLE) { + Assert.assertEquals(actualFieldNames, expected, "No accessibility failures:"); + } + } + else { + Assert.assertEquals(actualFieldNames, expected, "Accessibility failures differ:"); + } + } + else { + if (!actual.values().stream().allMatch(IllegalAccessException.class::isInstance)) { + Assert.fail("Expecting an IllegalArgumentException for all failures " + actual); + } + } + } + + static List fields(Class src) { + return List.of(src.getDeclaredFields()); + } +} diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/A.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/A.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, 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. + */ + +package pkg; + + +import java.lang.invoke.MethodHandles; +import java.util.Set; + +public class A { + public static Object f_public_static; + protected static Object f_protected_static; + static /*package*/ Object f_package_static; + private static Object f_private_static; + + public static final Object f_public_static_final = null; + protected static final Object f_protected_static_final = null; + static /*package*/ final Object f_package_static_final = null; + private static final Object f_private_static_final = null; + + public Object f_public; + protected Object f_protected; + /*package*/ Object f_package; + private Object f_private; + + public final Object f_public_final = null; + protected final Object f_protected_final = null; + /*package*/ final Object f_package_final = null; + private final Object f_private_final = null; + + // + + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Set inaccessibleFields() { + // All fields of pkg.A are accessible to itself + return Set.of(); + } +} diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/B_extends_A.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/B_extends_A.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016, 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. + */ + +package pkg; + + +import java.lang.invoke.MethodHandles; +import java.util.Set; + +public class B_extends_A extends A { + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Set inaccessibleFields() { + // Only private fields of pkg.A are not accessible to subclass pkg.B + // Note: protected fields are also package accessible + return Set.of( + "f_private", + "f_private_final", + "f_private_static", + "f_private_static_final" + ); + } +} diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/C.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/C.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, 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. + */ + +package pkg; + + +import java.lang.invoke.MethodHandles; +import java.util.Set; + +public class C { + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Set inaccessibleFields() { + // Only private fields of pkg.A are not accessible to independent + // class pkg.C + // Note: protected fields are also package accessible + return Set.of( + "f_private", + "f_private_final", + "f_private_static", + "f_private_static_final" + ); + } +} diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/subpkg/B_extends_A.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/subpkg/B_extends_A.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, 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. + */ + +package pkg.subpkg; + + +import pkg.A; + +import java.lang.invoke.MethodHandles; +import java.util.Set; + +public class B_extends_A extends A { + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Set inaccessibleFields() { + // Only public and protected fields of pkg.A are accessible to subclass + // pkg.subpkg.B + return Set.of( + "f_private", + "f_private_final", + "f_package", + "f_package_final", + "f_private_static", + "f_private_static_final", + "f_package_static", + "f_package_static_final" + ); + } +} diff -r 951bb58383a4 -r 86f49af093e5 jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/subpkg/C.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/invoke/VarHandles/accessibility/pkg/subpkg/C.java Mon Apr 11 19:00:49 2016 +0200 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 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. + */ + +package pkg.subpkg; + + +import java.lang.invoke.MethodHandles; +import java.util.Set; + +public class C { + public static MethodHandles.Lookup lookup() { + return MethodHandles.lookup(); + } + + public static Set inaccessibleFields() { + // Only public fields of pkg.A are accessible to independent + // class pkg.subpkg.C + return Set.of( + "f_private", + "f_private_final", + "f_protected", + "f_protected_final", + "f_package", + "f_package_final", + "f_private_static", + "f_private_static_final", + "f_protected_static", + "f_protected_static_final", + "f_package_static", + "f_package_static_final" + ); + } +}