Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(0.13.0) Fix VarHandleDesc constructor setup #5076

Merged
merged 1 commit into from
Mar 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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}."
Expand Down
138 changes: 101 additions & 37 deletions jcl/src/java.base/share/classes/java/lang/invoke/VarHandle.java
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ public boolean equals(Object obj) {
return false;
}


/* argument comparison */
VarHandle that = (VarHandle)obj;
if (!(this.fieldType.equals(that.fieldType)
Expand Down Expand Up @@ -1037,7 +1036,6 @@ MethodHandle getFromHandleTable(int operation) {
/* nominal descriptor of a VarHandle constant */
public static final class VarHandleDesc extends DynamicConstantDesc<VarHandle> implements ConstantDesc {
private Kind type = null;
private ClassDesc declaringClassDesc = null;

private static enum Kind {
ARRAY,
Expand All @@ -1051,76 +1049,94 @@ 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$
}

/**
* Creates a VarHandleDesc describing an instance field.
*
* @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);
}

/**
* Creates a VarHandleDesc describing a static field.
*
* @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);
}

/**
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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 */
}
Original file line number Diff line number Diff line change
@@ -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<VarHandle> to a static VarHandle to DynamicConstantDesc<VarHandle> 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<VarHandle> desc = DynamicConstantDesc.ofNamed(bsm, name, ConstantDescs.CD_VarHandle, bsmargs);
VarHandle resolvedClass = (VarHandle)desc.resolveConstantDesc(MethodHandles.lookup());
DynamicConstantDesc<VarHandle> 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<VarHandle> desc = DynamicConstantDesc.ofNamed(ConstantDescs.BSM_VARHANDLE_ARRAY, "af", ConstantDescs.CD_VarHandle, new ConstantDesc[] { arrayClassDesc });
VarHandle resolvedClass = (VarHandle)desc.resolveConstantDesc(MethodHandles.lookup());
DynamicConstantDesc<VarHandle> 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());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading