diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index c15136ced44..404cdb53f61 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -114,52 +114,53 @@ final class VarHandles { if (UNSAFE.shouldBeInitialized(refc)) UNSAFE.ensureClassInitialized(refc); + Class decl = f.getDeclaringClass(); Object base = MethodHandleNatives.staticFieldBase(f); long foffset = MethodHandleNatives.staticFieldOffset(f); if (!type.isPrimitive()) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleReferences.FieldStaticReadOnly(refc, base, foffset, type) - : new VarHandleReferences.FieldStaticReadWrite(refc, base, foffset, type)); + ? new VarHandleReferences.FieldStaticReadOnly(decl, base, foffset, type) + : new VarHandleReferences.FieldStaticReadWrite(decl, base, foffset, type)); } else if (type == boolean.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleBooleans.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleBooleans.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleBooleans.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleBooleans.FieldStaticReadWrite(decl, base, foffset)); } else if (type == byte.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleBytes.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleBytes.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleBytes.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleBytes.FieldStaticReadWrite(decl, base, foffset)); } else if (type == short.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleShorts.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleShorts.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleShorts.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleShorts.FieldStaticReadWrite(decl, base, foffset)); } else if (type == char.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleChars.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleChars.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleChars.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleChars.FieldStaticReadWrite(decl, base, foffset)); } else if (type == int.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleInts.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleInts.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleInts.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleInts.FieldStaticReadWrite(decl, base, foffset)); } else if (type == long.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleLongs.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleLongs.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleLongs.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleLongs.FieldStaticReadWrite(decl, base, foffset)); } else if (type == float.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleFloats.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleFloats.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleFloats.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleFloats.FieldStaticReadWrite(decl, base, foffset)); } else if (type == double.class) { return maybeAdapt(f.isFinal() && !isWriteAllowedOnFinalFields - ? new VarHandleDoubles.FieldStaticReadOnly(refc, base, foffset) - : new VarHandleDoubles.FieldStaticReadWrite(refc, base, foffset)); + ? new VarHandleDoubles.FieldStaticReadOnly(decl, base, foffset) + : new VarHandleDoubles.FieldStaticReadWrite(decl, base, foffset)); } else { throw new UnsupportedOperationException(); @@ -183,10 +184,10 @@ final class VarHandles { } // Required by instance static field handles - static Field getStaticFieldFromBaseAndOffset(Class receiverType, + static Field getStaticFieldFromBaseAndOffset(Class declaringClass, long offset, Class fieldType) { - for (Field f : receiverType.getDeclaredFields()) { + for (Field f : declaringClass.getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) continue; if (offset == UNSAFE.staticFieldOffset(f)) { diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template index fdde3aef663..36a646e6398 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2023, 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 @@ -382,21 +382,21 @@ final class VarHandle$Type$s { static sealed class FieldStaticReadOnly extends VarHandle { - final Class receiverType; + final Class declaringClass; final Object base; final long fieldOffset; #if[Object] final Class fieldType; #end[Object] - FieldStaticReadOnly(Class receiverType, Object base, long fieldOffset{#if[Object]?, Class fieldType}) { - this(receiverType, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadOnly.FORM, false); + FieldStaticReadOnly(Class declaringClass, Object base, long fieldOffset{#if[Object]?, Class fieldType}) { + this(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadOnly.FORM, false); } - protected FieldStaticReadOnly(Class receiverType, Object base, long fieldOffset{#if[Object]?, Class fieldType}, + protected FieldStaticReadOnly(Class declaringClass, Object base, long fieldOffset{#if[Object]?, Class fieldType}, VarForm form, boolean exact) { super(form, exact); - this.receiverType = receiverType; + this.declaringClass = declaringClass; this.base = base; this.fieldOffset = fieldOffset; #if[Object] @@ -408,14 +408,14 @@ final class VarHandle$Type$s { public FieldStaticReadOnly withInvokeExactBehavior() { return hasInvokeExactBehavior() ? this - : new FieldStaticReadOnly(receiverType, base, fieldOffset{#if[Object]?, fieldType}, vform, true); + : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, vform, true); } @Override public FieldStaticReadOnly withInvokeBehavior() { return !hasInvokeExactBehavior() ? this - : new FieldStaticReadOnly(receiverType, base, fieldOffset{#if[Object]?, fieldType}, vform, false); + : new FieldStaticReadOnly(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, vform, false); } @Override @@ -426,11 +426,11 @@ final class VarHandle$Type$s { // Reflect on this VarHandle to extract the field name var staticField = VarHandles.getStaticFieldFromBaseAndOffset( - receiverType, fieldOffset, {#if[Object]?fieldType:$type$.class}); - var receiverTypeRef = staticField.getDeclaringClass().describeConstable(); - if (!receiverTypeRef.isPresent()) + declaringClass, fieldOffset, {#if[Object]?fieldType:$type$.class}); + var declaringTypeRef = declaringClass.describeConstable(); + if (!declaringTypeRef.isPresent()) return Optional.empty(); - return Optional.of(VarHandleDesc.ofStaticField(receiverTypeRef.get(), staticField.getName(), fieldTypeRef.get())); + return Optional.of(VarHandleDesc.ofStaticField(declaringTypeRef.get(), staticField.getName(), fieldTypeRef.get())); } @Override @@ -471,27 +471,27 @@ final class VarHandle$Type$s { static final class FieldStaticReadWrite extends FieldStaticReadOnly { - FieldStaticReadWrite(Class receiverType, Object base, long fieldOffset{#if[Object]?, Class fieldType}) { - this(receiverType, base, fieldOffset{#if[Object]?, fieldType}, false); + FieldStaticReadWrite(Class declaringClass, Object base, long fieldOffset{#if[Object]?, Class fieldType}) { + this(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, false); } - private FieldStaticReadWrite(Class receiverType, Object base, long fieldOffset{#if[Object]?, Class fieldType}, + private FieldStaticReadWrite(Class declaringClass, Object base, long fieldOffset{#if[Object]?, Class fieldType}, boolean exact) { - super(receiverType, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadWrite.FORM, exact); + super(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadWrite.FORM, exact); } @Override public FieldStaticReadWrite withInvokeExactBehavior() { return hasInvokeExactBehavior() ? this - : new FieldStaticReadWrite(receiverType, base, fieldOffset{#if[Object]?, fieldType}, true); + : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, true); } @Override public FieldStaticReadWrite withInvokeBehavior() { return !hasInvokeExactBehavior() ? this - : new FieldStaticReadWrite(receiverType, base, fieldOffset{#if[Object]?, fieldType}, false); + : new FieldStaticReadWrite(declaringClass, base, fieldOffset{#if[Object]?, fieldType}, false); } @ForceInline diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java new file mode 100644 index 00000000000..ecda61cac48 --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/DescribeConstableTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023, 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 8302260 + * @build p.C p.D p.I p.q.Q + * @run junit DescribeConstableTest + * @summary Test VarHandle::describeConstable on static fields + */ + +import java.lang.constant.ClassDesc; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; +import java.lang.invoke.VarHandle.VarHandleDesc; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.Arguments; +import static org.junit.jupiter.api.Assertions.*; + +public class DescribeConstableTest { + private static final Lookup LOOKUP = MethodHandles.lookup(); + private static Stream testCases() { + return Stream.of( + // static field defined in p.C only + Arguments.of(p.C.class, "cString", String.class, p.C.class, "CClass"), + // static fields defined in both superinterface p.I and superclass p.q.Q + // resolved to the one defined in the direct superinterface of C + Arguments.of(p.C.class, "stringField", String.class, p.I.class, "I"), + Arguments.of(p.C.class, "longField", long.class, p.I.class, 10L), + // static fields defined in superclass p.q.Q only + Arguments.of(p.C.class, "stringField2", String.class, p.q.Q.class, "QClass2"), + Arguments.of(p.C.class, "longField2", long.class, p.q.Q.class, 102L), + // static fields defined in superinterface p.I only + Arguments.of(p.C.class, "stringField3", String.class, p.I.class, "I3"), + Arguments.of(p.C.class, "longField3", long.class, p.I.class, 13L), + // static fields defined in p.D only + Arguments.of(p.D.class, "dString", String.class, p.D.class, "DClass"), + Arguments.of(p.D.class, "dLong", long.class, p.D.class, 1L), + // static fields defined in both superinterface p.I and superclass p.q.Q + // resolved to the one defined in the direct superinterface of D + Arguments.of(p.D.class, "stringField", String.class, p.I.class, "I"), + Arguments.of(p.D.class, "longField", long.class, p.I.class, 10L) + ); + } + + @ParameterizedTest + @MethodSource("testCases") + void test(Class refc, String name, Class type, Class declaringClass, Object value) throws Throwable { + var vh = LOOKUP.findStaticVarHandle(refc, name, type); + assertEquals(value, vh.get()); + + var refcDesc = refc.describeConstable().orElseThrow(); + var typeDesc = type.describeConstable().orElseThrow(); + var vhd = vh.describeConstable().orElseThrow(); + var vhd2 = VarHandleDesc.ofStaticField(refcDesc, name, typeDesc); + + assertEquals(value, vhd.resolveConstantDesc(LOOKUP).get()); + assertEquals(value, vhd2.resolveConstantDesc(LOOKUP).get()); + + assertEquals(vhd.toString(), varHandleDescString(declaringClass, name, type, true)); + } + + static String varHandleDescString(Class declaringClass, String name, Class type, boolean staticField) { + return String.format("VarHandleDesc[%s%s.%s:%s]", + staticField ? "static " : "", + declaringClass.describeConstable().orElseThrow().displayName(), name, + type.describeConstable().orElseThrow().displayName()); + } +} diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java new file mode 100644 index 00000000000..353bd26dcf5 --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/C.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023, 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 p; + +public class C extends p.q.Q implements I { + public static String cString = "CClass"; +} diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/D.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/D.java new file mode 100644 index 00000000000..e82ef217b0d --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/D.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023, 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 p; + +public class D extends p.q.Q implements I { + public static String dString = "DClass"; + public static long dLong = 1L; +} diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/I.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/I.java new file mode 100644 index 00000000000..8fce020f4d2 --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/I.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, 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 p; + +public interface I { + String stringField = "I"; + long longField = 10l; + + String stringField3 = "I3"; + long longField3 = 13L; +} diff --git a/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java new file mode 100644 index 00000000000..b3bbb8adafc --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/describeConstable/p/q/Q.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023, 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 p.q; + +public class Q { + public static String stringField = "QClass"; + public static long longField = 100L; + + public static String stringField2 = "QClass2"; + public static long longField2 = 102L; +}