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

Methods and tests for jep334 Class #4157

Merged
merged 1 commit into from Feb 22, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 61 additions & 4 deletions jcl/src/java.base/share/classes/java/lang/Class.java
Expand Up @@ -4561,20 +4561,77 @@ public Class<?>[] getNestMembers() throws LinkageError, SecurityException {
/*[ENDIF] Java11 */

/*[IF Java12]*/
/**
* Create class of an array. The component type will be this Class instance.
*
* @return array class where the component type is this Class instance
*/
public Class<?> arrayType() {
throw new UnsupportedOperationException("Stub for Java 12 compilation (Jep334)");
if (this == void.class) {
throw new IllegalArgumentException();
}
return arrayTypeImpl();
}

private native Class<?> arrayTypeImpl();

/**
* Answers a Class object which represents the receiver's component type if the receiver
* represents an array type. The component type of an array type is the type of the elements
* of the array.
*
* @return the component type of the receiver. Returns null if the receiver does
* not represent an array.
*/
public Class<?> componentType() {
throw new UnsupportedOperationException("Stub for Java 12 compilation (Jep334)");
return getComponentType();
}

/**
* Returns the nominal descriptor of this Class instance, or an empty Optional
* if construction is not possible.
*
* @return Optional with a nominal descriptor of Class instance
*/
public Optional<ClassDesc> describeConstable() {
throw new UnsupportedOperationException("Stub for Java 12 compilation (Jep334)");
ClassDesc classDescriptor = ClassDesc.ofDescriptor(this.descriptorString());
return Optional.of(classDescriptor);
}

/**
* Return field descriptor of Class instance.
*
* @return field descriptor of Class instance
*/
public String descriptorString() {
throw new UnsupportedOperationException("Stub for Java 12 compilation (Jep334)");
/* see MethodType.getBytecodeStringName */

if (this.isPrimitive()) {
if (this == int.class) {
return "I"; //$NON-NLS-1$
} else if (this == long.class) {
return "J"; //$NON-NLS-1$
} else if (this == byte.class) {
return "B"; //$NON-NLS-1$
} else if (this == boolean.class) {
return "Z"; //$NON-NLS-1$
} else if (this == void.class) {
return "V"; //$NON-NLS-1$
} else if (this == char.class) {
return "C"; //$NON-NLS-1$
} else if (this == double.class) {
return "D"; //$NON-NLS-1$
} else if (this == float.class) {
return "F"; //$NON-NLS-1$
} else if (this == short.class) {
return "S"; //$NON-NLS-1$
}
}
String name = this.getName().replace('.', '/');
if (this.isArray()) {
return name;
}
return "L"+ name + ";"; //$NON-NLS-1$ //$NON-NLS-2$
}
/*[ENDIF] Java12 */
}
3 changes: 2 additions & 1 deletion runtime/oti/VMHelpers.hpp
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 1991, 2018 IBM Corp. and others
* Copyright (c) 1991, 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
Expand Down Expand Up @@ -165,6 +165,7 @@ typedef enum {
J9_BCLOOP_SEND_TARGET_VARHANDLE,
J9_BCLOOP_SEND_TARGET_INL_THREAD_ON_SPIN_WAIT,
J9_BCLOOP_SEND_TARGET_OUT_OF_LINE_INL,
J9_BCLOOP_SEND_TARGET_CLASS_ARRAY_TYPE_IMPL,
} VM_SendTarget;

typedef enum {
Expand Down
23 changes: 23 additions & 0 deletions runtime/vm/BytecodeInterpreter.hpp
Expand Up @@ -2721,6 +2721,26 @@ done:;
return EXECUTE_BYTECODE;
}

/* java.lang.Class: private native Class<?> arrayTypeImpl(); */
VMINLINE VM_BytecodeAction
inlClassArrayTypeImpl(REGISTER_ARGS_LIST)
{
VM_BytecodeAction rc = EXECUTE_BYTECODE;
J9Class *componentClazz = J9VM_J9CLASS_FROM_HEAPCLASS(_currentThread, *(j9object_t*)_sp);
theresa-m marked this conversation as resolved.
Show resolved Hide resolved
gacholio marked this conversation as resolved.
Show resolved Hide resolved
J9Class *arrayClazz = componentClazz->arrayClass;
if (NULL == arrayClazz) {
arrayClazz = internalCreateArrayClass(_currentThread,
(J9ROMArrayClass *) J9ROMIMAGEHEADER_FIRSTCLASS(_currentThread->javaVM->arrayROMClasses),
componentClazz);
}
if (NULL == arrayClazz) {
rc = GOTO_THROW_CURRENT_EXCEPTION;
} else {
returnObjectFromINL(REGISTER_ARGS, J9VM_J9CLASS_TO_HEAPCLASS(arrayClazz), 1);
}
gacholio marked this conversation as resolved.
Show resolved Hide resolved
return rc;
}

/* java.lang.Class: private native String getSimpleNameImpl(); */
VMINLINE VM_BytecodeAction
inlClassGetSimpleNameImpl(REGISTER_ARGS_LIST)
Expand Down Expand Up @@ -8718,6 +8738,7 @@ done:;
JUMP_TABLE_ENTRY(J9_BCLOOP_SEND_TARGET_VARHANDLE),
JUMP_TABLE_ENTRY(J9_BCLOOP_SEND_TARGET_INL_THREAD_ON_SPIN_WAIT),
JUMP_TABLE_ENTRY(J9_BCLOOP_SEND_TARGET_OUT_OF_LINE_INL),
JUMP_TABLE_ENTRY(J9_BCLOOP_SEND_TARGET_CLASS_ARRAY_TYPE_IMPL),
};
#endif /* !defined(USE_COMPUTED_GOTO) */

Expand Down Expand Up @@ -9277,6 +9298,8 @@ runMethod: {
PERFORM_ACTION(inlThreadOnSpinWait(REGISTER_ARGS));
JUMP_TARGET(J9_BCLOOP_SEND_TARGET_OUT_OF_LINE_INL):
PERFORM_ACTION(outOfLineINL(REGISTER_ARGS));
JUMP_TARGET(J9_BCLOOP_SEND_TARGET_CLASS_ARRAY_TYPE_IMPL):
PERFORM_ACTION(inlClassArrayTypeImpl(REGISTER_ARGS));
#if !defined(USE_COMPUTED_GOTO)
default:
Assert_VM_unreachable();
Expand Down
22 changes: 21 additions & 1 deletion runtime/vm/FastJNI_java_lang_Class.cpp
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2001, 2018 IBM Corp. and others
* Copyright (c) 2001, 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
Expand Down Expand Up @@ -180,6 +180,24 @@ Fast_java_lang_Class_getModifiersImpl(J9VMThread *currentThread, j9object_t rece
return (jint)modifiers;
}

/* java.lang.Class: private native Class<?> arrayTypeImpl(); */
j9object_t JNICALL
Fast_java_lang_Class_arrayTypeImpl(J9VMThread *currentThread, j9object_t receiverObject)
{
j9object_t arrayObject = NULL;
J9Class *componentClazz = J9VM_J9CLASS_FROM_HEAPCLASS(currentThread, receiverObject);
theresa-m marked this conversation as resolved.
Show resolved Hide resolved
J9Class *arrayClazz = componentClazz->arrayClass;
if (NULL == arrayClazz) {
arrayClazz = internalCreateArrayClass(currentThread,
(J9ROMArrayClass *) J9ROMIMAGEHEADER_FIRSTCLASS(currentThread->javaVM->arrayROMClasses),
componentClazz);
}
if (NULL != arrayClazz) {
arrayObject = J9VM_J9CLASS_TO_HEAPCLASS(arrayClazz);
}
return arrayObject;
}

/* java.lang.Class: public native Class<?> getComponentType(); */
j9object_t JNICALL
Fast_java_lang_Class_getComponentType(J9VMThread *currentThread, j9object_t receiverObject)
Expand Down Expand Up @@ -212,6 +230,8 @@ J9_FAST_JNI_METHOD_TABLE(java_lang_Class)
J9_FAST_JNI_METHOD("getComponentType", "()Ljava/lang/Class;", Fast_java_lang_Class_getComponentType,
J9_FAST_JNI_RETAIN_VM_ACCESS | J9_FAST_JNI_NOT_GC_POINT | J9_FAST_JNI_NO_NATIVE_METHOD_FRAME | J9_FAST_JNI_NO_EXCEPTION_THROW |
J9_FAST_JNI_NO_SPECIAL_TEAR_DOWN | J9_FAST_JNI_DO_NOT_WRAP_OBJECTS)
J9_FAST_JNI_METHOD("arrayTypeImpl", "()Ljava/lang/Class;", Fast_java_lang_Class_arrayTypeImpl,
J9_FAST_JNI_RETAIN_VM_ACCESS | J9_FAST_JNI_DO_NOT_WRAP_OBJECTS)
J9_FAST_JNI_METHOD_TABLE_END

}
1 change: 1 addition & 0 deletions runtime/vm/bindnatv.cpp
Expand Up @@ -83,6 +83,7 @@ static inlMapping mappings[] = {
{ "Java_java_lang_Class_isPrimitive__", J9_BCLOOP_SEND_TARGET_INL_CLASS_IS_PRIMITIVE },
{ "Java_java_lang_Class_getModifiersImpl__", J9_BCLOOP_SEND_TARGET_INL_CLASS_GET_MODIFIERS_IMPL },
{ "Java_java_lang_Class_getComponentType__", J9_BCLOOP_SEND_TARGET_INL_CLASS_GET_COMPONENT_TYPE },
{ "Java_java_lang_Class_arrayTypeImpl__", J9_BCLOOP_SEND_TARGET_CLASS_ARRAY_TYPE_IMPL },
{ "Java_java_lang_System_arraycopy__Ljava_lang_Object_2ILjava_lang_Object_2II", J9_BCLOOP_SEND_TARGET_INL_SYSTEM_ARRAYCOPY },
{ "Java_java_lang_System_currentTimeMillis__", J9_BCLOOP_SEND_TARGET_INL_SYSTEM_CURRENT_TIME_MILLIS },
{ "Java_java_lang_System_nanoTime__", J9_BCLOOP_SEND_TARGET_INL_SYSTEM_NANO_TIME },
Expand Down
@@ -0,0 +1,176 @@
/*******************************************************************************
* Copyright (c) 2018, 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;

import java.lang.constant.ClassDesc;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.util.Optional;

import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.log4testng.Logger;

/**
* This test Java.lang.Class API added in Java 12 and later version.
*
* new methods:
* - describeConstable()
* - descriptorString()
* - arrayType()
* - componentType(): wrapper for Class.getComponentType(), not tested
*/
public class Test_Class {
public static Logger logger = Logger.getLogger(Test_Class.class);

Object[][] primitiveTest = {
{ boolean.class, "Z" },
{ byte.class, "B" },
{ char.class, "C" },
{ short.class, "S" },
{ int.class, "I" },
{ float.class, "F" },
{ double.class, "D" },
{ long.class, "J" },
{ void.class, "V" }
};

/* descriptor strings */
String objectResult = "Ljava/lang/Object;";
String arrayResult = "[D";
String arrayResult2 = "[[[D";

/* test classes */
Class<?> objectTest = new Object().getClass();
Class<?> arrayTest = new double[0].getClass();
Class<?> arrayTest2 = new double[0][][].getClass();
Runnable lambdaExp = () -> System.out.println("test");
Class<?> lambdaExpTest = lambdaExp.getClass();

/*
* Test Java 12 API Class.descriptorString()
*/
@Test(groups = { "level.sanity" })
public void testClassDescriptorString() {
/* primitive type test */
for (Object[] prim : primitiveTest) {
logger.debug("testClassDescriptorString: Primitive test result is: " + ((Class<?>)prim[0]).descriptorString() + " expected: " + prim[1]);
Assert.assertTrue(((Class<?>)prim[0]).descriptorString().equals(prim[1]));
}

/* ObjectType test */
logger.debug("testClassDescriptorString: Object test result is: " + objectTest.descriptorString() + " expected: " + objectResult);
Assert.assertTrue(objectTest.descriptorString().equals(objectResult));

/* ArrayType test */
logger.debug("testClassDescriptorString: Array test result is: " + arrayTest.descriptorString() + " expected: " + arrayResult);
Assert.assertTrue(arrayTest.descriptorString().equals(arrayResult));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something similar to the suggestion above can be done for the primitive array cases.... actually, the Object subclasses can be rolled into the same kind of tests


/* ArrayType test 2 */
logger.debug("testClassDescriptorString: Array test 2 result is: " + arrayTest2.descriptorString() + " expected: " + arrayResult2);
Assert.assertTrue(arrayTest2.descriptorString().equals(arrayResult2));

/*
* Lambda expression test. DescriptorString value will be different in each run.
* Verify that no error is thrown.
*/
logger.debug("testClassDescriptorString: Lambda test result is: " + lambdaExpTest.descriptorString());
}

/*
* Test Java 12 API Class.describeConstable()
*/
@Test(groups = { "level.sanity" })
public void testClassDescribeConstable() throws Throwable {
for (Object[] prim : primitiveTest) {
describeConstableTestGeneral("testClassDescribeConstable (primitive " + prim[1] + ")", (Class<?>)prim[0]);
}
describeConstableTestGeneral("testClassDescribeConstable (object)", objectTest);
describeConstableTestGeneral("testClassDescribeConstable (array)", arrayTest);
describeConstableTestGeneral("testClassDescribeConstable (array 2)", arrayTest2);
describeConstableTestLambda("testClassDescribeConstable (lambda expression)");
}

private void describeConstableTestGeneral(String testName, Class<?> testClass) throws Throwable {
Optional<ClassDesc> optionalDesc = testClass.describeConstable();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use .orElseThrow() and let the test code throw if the Optional is empty. The test harness knows how to fail tests for unexpected exceptions.

ClassDesc desc = optionalDesc.orElseThrow();

/*
* verify that descriptor can be resolved. Otherwise exception will be thrown.
*/
Class<?> resolvedClass = (Class<?>)desc.resolveConstantDesc(MethodHandles.lookup());

String originalDescriptor = testClass.descriptorString();
String newDescriptor = resolvedClass.descriptorString();
logger.debug(testName + ": Descriptor of original class is: " + originalDescriptor + " descriptor of ClassDesc is: " + newDescriptor);
Assert.assertTrue(testClass.equals(resolvedClass));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one last improvement to this line. previously was comparing strings

Assert.assertTrue(originalDescriptor.equals(newDescriptor));

}

/*
* seperate test case for lambda expression because resolveConstantDesc is
* expected to fail.
*/
private void describeConstableTestLambda(String testName) {
Optional<ClassDesc> optionalDesc = lambdaExpTest.describeConstable();
ClassDesc desc = optionalDesc.orElseThrow();

/*
* verify that descriptor can be resolved. Otherwise exception will be thrown.
*/
try {
Class<?> resolvedClass = (Class<?>)desc.resolveConstantDesc(MethodHandles.lookup());
Assert.fail(testName + " : resolveConstantDesc did not throw an error.");
} catch (Throwable e) {
/* resolveConstantDesc is expected to fail */
logger.debug(testName + ": exception thrown for resolveConstantDesc was " + e.toString());
}
}

/*
* Test Java 12 API Class.arrayType()
*/
@Test(groups = { "level.sanity" })
public void testClassArrayType() throws Throwable {
for (Object[] prim : primitiveTest) {
if ((Class<?>)prim[0] == void.class) {
/* test is expected to throw IllegalArgumentException */
try {
arrayTypeTestGeneral("testClassArrayType (primitive) this test should throw IllegalArgumentException", (Class<?>)prim[0], (String)prim[1]);
Assert.fail();
} catch(IllegalArgumentException e) {
/* test passed */
}
} else {
arrayTypeTestGeneral("testClassArrayType (primitive)", (Class<?>)prim[0], (String)prim[1]);
}
}
arrayTypeTestGeneral("testClassArrayType (object)", objectTest, objectResult);
arrayTypeTestGeneral("testClassArrayType (embedded array)", arrayTest, arrayResult);
}

private void arrayTypeTestGeneral(String testName, Class<?> testClass, String result) throws Throwable {
Class<?> array = testClass.arrayType();
String arrayString = array.descriptorString();
logger.debug(testName + ": Descriptor of component is: " + result + " descriptor of array is: " + arrayString);
Assert.assertTrue(arrayString.equals("[" + result));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test case for the class of a lambda expression?

Runnable r = () -> System.out.println("a");
// This should succeed
ClassDesc desc = r.getClass().describeConstanble();

desc.getDescriptorString() should also succeed but the value returned will be different in each run

but resolveConstantDesc(MethodHandles.lookup()) should fail

}
1 change: 1 addition & 0 deletions test/functional/Java12andUp/testng.xml
Expand Up @@ -28,6 +28,7 @@
<test name="Jep334Tests">
<classes>
<class name="org.openj9.test.java_lang.Test_String" />
<class name="org.openj9.test.java_lang.Test_Class" />
</classes>
</test>

Expand Down