diff --git a/jcl/src/java.base/share/classes/com/ibm/oti/util/ExternalMessages-MasterIndex.properties b/jcl/src/java.base/share/classes/com/ibm/oti/util/ExternalMessages-MasterIndex.properties index c1aaad47363..698d2b8302c 100644 --- a/jcl/src/java.base/share/classes/com/ibm/oti/util/ExternalMessages-MasterIndex.properties +++ b/jcl/src/java.base/share/classes/com/ibm/oti/util/ExternalMessages-MasterIndex.properties @@ -1185,6 +1185,7 @@ K0612="CompositeData contains an unexpected threadState value." K0613="{0} (loaded from {1} by {2}) called from {3} (loaded from {4} by {5})." +K0619="Malformated VarHandleDesc, {0} could not be retrieved." K0620="This VarHandle operation is not supported by type {0}." K0621="Index {0} is not within the bounds of the provided array of size {1}." K0622="Index {0}, array length {1}." diff --git a/jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java index a8fe450dbdf..c65775da388 100644 --- a/jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -351,7 +351,6 @@ public boolean equals(Object obj) { return false; } - /* argument comparison */ VarHandle that = (VarHandle)obj; if (!(this.fieldType.equals(that.fieldType) @@ -1037,7 +1036,6 @@ MethodHandle getFromHandleTable(int operation) { /* nominal descriptor of a VarHandle constant */ public static final class VarHandleDesc extends DynamicConstantDesc implements ConstantDesc { private Kind type = null; - private ClassDesc declaringClassDesc = null; private static enum Kind { ARRAY, @@ -1051,46 +1049,62 @@ DirectMethodHandleDesc getBootstrap() { result = ConstantDescs.BSM_VARHANDLE_ARRAY; break; case STATIC_FIELD: + result = ConstantDescs.BSM_VARHANDLE_STATIC_FIELD; + break; + case INSTANCE_FIELD: result = ConstantDescs.BSM_VARHANDLE_FIELD; break; + } + return result; + } + + ConstantDesc[] getBootstrapArgs(ClassDesc declaringClassDesc, ClassDesc varHandleDesc) { + ConstantDesc[] result = null; + switch(this) { + case ARRAY: + result = new ConstantDesc[] { varHandleDesc }; + break; + case STATIC_FIELD: case INSTANCE_FIELD: - result = ConstantDescs.BSM_VARHANDLE_STATIC_FIELD; + result = new ConstantDesc[] { declaringClassDesc, varHandleDesc }; break; } return result; } + + boolean isField() { + return !this.equals(ARRAY); + } } /** - * Create a nominal descriptor for a field VarHandle. + * Create a nominal descriptor for a VarHandle. * + * @param type kind of VarHandleDesc to be created * @param declaringClassDesc ClassDesc describing the declaring class for the field (null for array) - * @param name unqualified String name of the field (null for array) - * @param varHandleType ClassDesc describing the field type - * @throws NullPointerException if there is a null argument + * @param name unqualified String name of the field ("_" for array) + * @param varHandleType ClassDesc describing the field or array type + * @throws NullPointerException if name argument is null */ private VarHandleDesc(Kind type, ClassDesc declaringClassDesc, String name, ClassDesc varHandleDesc) throws NullPointerException { - super(type.getBootstrap(), name, varHandleDesc, ConstantDescs.CD_VarHandle); + super(type.getBootstrap(), name, ConstantDescs.CD_VarHandle, type.getBootstrapArgs(declaringClassDesc, varHandleDesc)); this.type = type; - this.declaringClassDesc = declaringClassDesc; } /** * Creates a VarHandleDesc describing a VarHandle for an array type. * - * @param arrayClass ClassDesc describing the array type + * @param arrayClassDesc ClassDesc describing the array type * @return VarHandleDesc describing a VarHandle for an array type * @throws NullPointerException if there is a null argument */ - public static VarHandleDesc ofArray(ClassDesc arrayClass) throws NullPointerException { - /* Verify that arrayClass is an array, and throw an error. Otherwise the call to componentType() - * will return null and a NullPointerException will be thrown which does not follow the spec. - */ - if (!arrayClass.isArray()) { + public static VarHandleDesc ofArray(ClassDesc arrayClassDesc) throws NullPointerException { + /* Verify that arrayClass is an array. This also covers the null check. */ + if (!arrayClassDesc.isArray()) { /*[MSG "K0625", "{0} is not an array type."]*/ - throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0625", arrayClass.descriptorString())); //$NON-NLS-1$ + throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0625", arrayClassDesc.descriptorString())); //$NON-NLS-1$ } - return new VarHandleDesc(Kind.ARRAY, arrayClass, "_", arrayClass.componentType()); //$NON-NLS-1$ + return new VarHandleDesc(Kind.ARRAY, null, "_", arrayClassDesc); //$NON-NLS-1$ } /** @@ -1098,14 +1112,15 @@ public static VarHandleDesc ofArray(ClassDesc arrayClass) throws NullPointerExce * * @param declaringClassDesc ClassDesc describing the declaring class for the field * @param name unqualified String name of the field - * @param fieldType ClassDesc describing the field type + * @param fieldTypeDesc ClassDesc describing the field type * @return VarHandleDesc describing the instance field * @throws NullPointerException if there is a null argument */ - public static VarHandleDesc ofField(ClassDesc declaringClassDesc, String name, ClassDesc fieldType) throws NullPointerException { - /* other fields will be null checked in constructor */ + public static VarHandleDesc ofField(ClassDesc declaringClassDesc, String name, ClassDesc fieldTypeDesc) throws NullPointerException { + /* name null check is in the constructor */ Objects.requireNonNull(declaringClassDesc); - return new VarHandleDesc(Kind.INSTANCE_FIELD, declaringClassDesc, name, fieldType); + Objects.requireNonNull(fieldTypeDesc); + return new VarHandleDesc(Kind.INSTANCE_FIELD, declaringClassDesc, name, fieldTypeDesc); } /** @@ -1113,14 +1128,15 @@ public static VarHandleDesc ofField(ClassDesc declaringClassDesc, String name, C * * @param declaringClassDesc ClassDesc describing the declaring class for the field * @param name unqualified String name of the field - * @param fieldType ClassDesc describing the field type + * @param fieldTypeDesc ClassDesc describing the field type * @return VarHandleDesc describing the static field * @throws NullPointerException if there is a null argument */ - public static VarHandleDesc ofStaticField(ClassDesc declaringClassDesc, String name, ClassDesc fieldType) throws NullPointerException { - /* other fields will be null checked in constructor */ + public static VarHandleDesc ofStaticField(ClassDesc declaringClassDesc, String name, ClassDesc fieldTypeDesc) throws NullPointerException { + /* name null check is in the constructor */ Objects.requireNonNull(declaringClassDesc); - return new VarHandleDesc(Kind.STATIC_FIELD, declaringClassDesc, name, fieldType); + Objects.requireNonNull(fieldTypeDesc); + return new VarHandleDesc(Kind.STATIC_FIELD, declaringClassDesc, name, fieldTypeDesc); } /** @@ -1134,17 +1150,16 @@ public static VarHandleDesc ofStaticField(ClassDesc declaringClassDesc, String n public VarHandle resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException { VarHandle result; - /* resolve declaringClassDesc, which is array class for array type */ - Class declaringClass = (Class)declaringClassDesc.resolveConstantDesc(lookup); - switch(type) { case ARRAY: - result = MethodHandles.arrayElementVarHandle(declaringClass); + Class arrayClass = (Class)getArrayTypeClassDesc().resolveConstantDesc(lookup); + result = MethodHandles.arrayElementVarHandle(arrayClass); break; case STATIC_FIELD: case INSTANCE_FIELD: - /* resolve field descriptor and field name */ - Class fieldClass = (Class)constantType().resolveConstantDesc(lookup); + /* resolve declaring class, field descriptor and field name */ + Class declaringClass = (Class)getFieldDeclaringClassDesc().resolveConstantDesc(lookup); + Class fieldClass = (Class)getFieldTypeClassDesc().resolveConstantDesc(lookup); String name = constantName(); try { @@ -1158,7 +1173,8 @@ public VarHandle resolveConstantDesc(MethodHandles.Lookup lookup) throws Reflect } break; default: - throw new java.lang.InternalError(); + /*[MSG "K0619", "Malformated VarHandleDesc, {0} could not be retrieved."]*/ + throw new InternalError(com.ibm.oti.util.Msg.getString("K0619", "field type")); //$NON-NLS-1$ //$NON-NLS-2$ } return result; @@ -1172,19 +1188,19 @@ public VarHandle resolveConstantDesc(MethodHandles.Lookup lookup) throws Reflect */ @Override public String toString() { - - /* VarHandleDesc[(static) (declaring class name).(constant field):(constant type)] */ + /* VarHandleDesc[(static) (declaring class name).(constant field):(var type)] */ String structure = "VarHandleDesc[%s]"; //$NON-NLS-1$ String content = ""; //$NON-NLS-1$ if (type.equals(Kind.ARRAY)) { /* array type is declaring class, varType is the component type of the array */ - content = String.format("%s[]", declaringClassDesc.displayName()); //$NON-NLS-1$ + content = String.format("%s[]", getArrayTypeClassDesc().displayName()); //$NON-NLS-1$ } else { if (type.equals(Kind.STATIC_FIELD)) { content += "static "; //$NON-NLS-1$ } - content = String.format(content + "%s.%s:%s", declaringClassDesc.displayName(), constantName(), varType().displayName()); //$NON-NLS-1$ + content = String.format(content + "%s.%s:%s", getFieldDeclaringClassDesc().displayName(), //$NON-NLS-1$ + constantName(), getFieldTypeClassDesc().displayName()); } return String.format(structure, content); @@ -1196,8 +1212,56 @@ public String toString() { * @return ClassDesc of variable type */ public ClassDesc varType() { - return constantType(); + return type.isField() ? getFieldTypeClassDesc() : getArrayTypeClassDesc().componentType(); } + + /** + * Helper to retrieve declaring class descriptor from superclass bootstrap arguments. + * + * @return ClassDesc declaring class desccriptor + * @throws InternalError if VarHandleDesc was created improperly or instance is not a field + */ + private ClassDesc getFieldDeclaringClassDesc() { + ConstantDesc[] args = bootstrapArgs(); + /* declaring class is always the first bootstrap argument */ + if (!type.isField() || (args.length < 2) || !(args[0] instanceof ClassDesc)) { + /*[MSG "K0619", "Malformated VarHandleDesc, {0} could not be retrieved."]*/ + throw new InternalError(com.ibm.oti.util.Msg.getString("K0619", "field declaring class descriptor")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return (ClassDesc)args[0]; + } + + /** + * Helper to retrieve field type descriptor from superclass bootstrap arguments. + * + * @return ClassDesc field type desccriptor + * @throws InternalError if VarHandleDesc was created improperly or instance is not a field + */ + private ClassDesc getFieldTypeClassDesc() { + ConstantDesc[] args = bootstrapArgs(); + /* field type is always the second bootstrap argument */ + if (!type.isField() || (args.length < 2) || !(args[1] instanceof ClassDesc)) { + /*[MSG "K0619", "Malformated VarHandleDesc, {0} could not be retrieved."]*/ + throw new InternalError(com.ibm.oti.util.Msg.getString("K0619", "field class descriptor")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return (ClassDesc)args[1]; + } + + /** + * Helper to retrieve array type descriptor from superclass bootstrap arguments. + * + * @return ClassDesc array type descriptor + * @throws InternalError if VarHandleDesc was created improperly or instance is not an array type + */ + private ClassDesc getArrayTypeClassDesc() { + ConstantDesc[] args = bootstrapArgs(); + /* array type is always the first bootstrap argument for an array type */ + if (type.isField() || (args.length == 0) || !(args[0] instanceof ClassDesc)) { + /*[MSG "K0619", "Malformated VarHandleDesc, {0} could not be retrieved."]*/ + throw new InternalError(com.ibm.oti.util.Msg.getString("K0619", "array type descriptor")); //$NON-NLS-1$ //$NON-NLS-2$ + } + return (ClassDesc)args[0]; + } } /*[ENDIF] Java12 */ } diff --git a/test/functional/Java12andUp/src/org/openj9/test/java_lang_constant/Test_DynamicConstantDesc.java b/test/functional/Java12andUp/src/org/openj9/test/java_lang_constant/Test_DynamicConstantDesc.java new file mode 100644 index 00000000000..dc2a85a17aa --- /dev/null +++ b/test/functional/Java12andUp/src/org/openj9/test/java_lang_constant/Test_DynamicConstantDesc.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2019, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ +package org.openj9.test.java_lang_constant; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.log4testng.Logger; + +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.DirectMethodHandleDesc; +import java.lang.constant.DynamicConstantDesc; +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.Arrays; + +/** + * This test Java.lang.constant.DynamicConstantDesc API added in Java 12 and + * later versions. + * + * tested methods: + * - resolveConstantDesc for primitive and VarHandle types + */ +public class Test_DynamicConstantDesc { + public static Logger logger = Logger.getLogger(Test_DynamicConstantDesc.class); + + /* + * Test Java 12 API DynamicConstantDesc.resolveConstantDesc() + */ + @Test(groups = { "level.sanity" }) + public void testDynamicConstantDescResolveConstantDescPrimitive() throws Throwable { + /* describe and resolve constant */ + DynamicConstantDesc desc = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_PRIMITIVE_CLASS, "I", + ConstantDescs.CD_Class, new ConstantDesc[0]); + Class resolvedClass = (Class)desc.resolveConstantDesc(MethodHandles.lookup()); + DynamicConstantDesc resDesc = (DynamicConstantDesc)resolvedClass.describeConstable().orElseThrow(); + + /* verify that constant was resolve properly */ + logger.debug("testDynamicConstantDescResolveConstantDesc: resolved class is: " + resolvedClass.toString()); + Assert.assertTrue(resolvedClass.equals(int.class)); + + /* test equals */ + logger.debug("original is: " + desc.toString() + " resolved is: " + resDesc.toString()); + Assert.assertTrue(desc.equals(resDesc)); + } + + /* helpers for VarHandle tests */ + private static ClassDesc helperClass = + ClassDesc.of("org.openj9.test.java_lang_constant", "Test_DynamicConstantDesc").nested("NestedHelper"); + + static class NestedHelper { + static int sf; + int f; + int[] af; + } + + /* + * Convert DynamicConstantDesc to a static VarHandle to DynamicConstantDesc and verify + * that the descriptors are equal. + */ + private void testDynamicConstantDescResolveConstantDescVarHandleField(String testName, DirectMethodHandleDesc bsm, + String name, ConstantDesc[] bsmargs) throws Throwable { + /* describe and resolve constant */ + DynamicConstantDesc desc = DynamicConstantDesc.ofNamed(bsm, name, ConstantDescs.CD_VarHandle, bsmargs); + VarHandle resolvedClass = (VarHandle)desc.resolveConstantDesc(MethodHandles.lookup()); + DynamicConstantDesc resDesc = resolvedClass.describeConstable().orElseThrow(); + + /* test if descriptors are equals */ + logger.debug(testName + ": original is: " + desc.toString() + " resolved is: " + resDesc.toString()); + Assert.assertTrue(desc.equals(resDesc)); + } + + @Test(groups = { "level.sanity" }) + public void testDynamicConstantDescResolveConstantDescVarHandleStatic() throws Throwable { + testDynamicConstantDescResolveConstantDescVarHandleField("testDynamicConstantDescResolveConstantDescVarHandleStatic", + ConstantDescs.BSM_VARHANDLE_STATIC_FIELD, "sf", new ConstantDesc[] {helperClass, ConstantDescs.CD_int }); + } + + @Test(groups = { "level.sanity" }) + public void testDynamicConstantDescResolveConstantDescVarHandleInstance() throws Throwable { + testDynamicConstantDescResolveConstantDescVarHandleField("testDynamicConstantDescResolveConstantDescVarHandleInstance", + ConstantDescs.BSM_VARHANDLE_FIELD, "f", new ConstantDesc[] {helperClass, ConstantDescs.CD_int }); + } + + @Test(groups = { "level.sanity" }) + public void testDynamicConstantDescResolveConstantDescVarHandleArray() throws Throwable { + ClassDesc arrayClassDesc = int[].class.describeConstable().orElseThrow(); + + /* describe and resolve constant */ + DynamicConstantDesc desc = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_ARRAY, "af", ConstantDescs.CD_VarHandle, new ConstantDesc[] { arrayClassDesc }); + VarHandle resolvedClass = (VarHandle)desc.resolveConstantDesc(MethodHandles.lookup()); + DynamicConstantDesc resDesc = resolvedClass.describeConstable().orElseThrow(); + + /* test if descriptors are equals */ + logger.debug("testDynamicConstantDescResolveConstantDescVarHandleArray" + ": original is: " + desc.toString() + " resolved desc is: " + resDesc.toString()); + + /* test equivalence manually, descriptor names will not be equal */ + Assert.assertTrue(Arrays.equals(desc.bootstrapArgs(), resDesc.bootstrapArgs())); + Assert.assertTrue(desc.bootstrapMethod().equals(resDesc.bootstrapMethod())); + Assert.assertEquals(resDesc.constantName(), "_"); + Assert.assertEquals(desc.constantType(), resDesc.constantType()); + + } +} diff --git a/test/functional/Java12andUp/src/org/openj9/test/java_lang_invoke/Test_VarHandleDesc.java b/test/functional/Java12andUp/src/org/openj9/test/java_lang_invoke/Test_VarHandleDesc.java index c1c799ac503..168e8d3b2d9 100644 --- a/test/functional/Java12andUp/src/org/openj9/test/java_lang_invoke/Test_VarHandleDesc.java +++ b/test/functional/Java12andUp/src/org/openj9/test/java_lang_invoke/Test_VarHandleDesc.java @@ -217,7 +217,7 @@ public void testVarHandleDescResolveConstantDesc() throws Throwable { private void resolveConstantDescTestGeneric(String testName, VarHandle handle) throws Throwable { VarHandleDesc handleDesc = handle.describeConstable().orElseThrow(); - VarHandle resolvedHandle = resolvedHandle = handleDesc.resolveConstantDesc(MethodHandles.lookup()); + VarHandle resolvedHandle = handleDesc.resolveConstantDesc(MethodHandles.lookup()); logger.debug(testName + " is running."); Assert.assertTrue(handle.equals(resolvedHandle)); diff --git a/test/functional/Java12andUp/testng.xml b/test/functional/Java12andUp/testng.xml index 50ab0cd205f..8140eb83670 100644 --- a/test/functional/Java12andUp/testng.xml +++ b/test/functional/Java12andUp/testng.xml @@ -33,6 +33,7 @@ +