Skip to content

Commit

Permalink
Merge pull request #15044 from thallium/master
Browse files Browse the repository at this point in the history
Tests for final fields of hidden class, regular class, and interface
  • Loading branch information
tajila committed May 12, 2022
2 parents 8578238 + 80f1061 commit 91e41ae
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 4 deletions.
44 changes: 43 additions & 1 deletion test/functional/Java15andUp/playlist.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
Copyright (c) 2020, 2021 IBM Corp. and others
Copyright (c) 2020, 2022 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 @@ -61,4 +61,46 @@
<version>15+</version>
</versions>
</test>
<test>
<testCaseName>Jep371Tests</testCaseName>
<command>$(JAVA_COMMAND) $(JVM_OPTIONS) \
--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED \
-cp $(Q)$(LIB_DIR)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \
-cp $(Q)$(LIB_DIR)$(D)asm.jar$(P)$(LIB_DIR)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \
org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames Jep371Tests \
-groups $(TEST_GROUP) \
-excludegroups $(DEFAULT_EXCLUDE); \
$(TEST_STATUS)
</command>
<levels>
<level>sanity</level>
</levels>
<groups>
<group>functional</group>
</groups>
<versions>
<version>15+</version>
</versions>
</test>
<test>
<testCaseName>RegularClassAndInterfaceFinalFieldTests</testCaseName>
<command>$(JAVA_COMMAND) $(JVM_OPTIONS) \
--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED \
-cp $(Q)$(LIB_DIR)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \
-cp $(Q)$(LIB_DIR)$(D)asm.jar$(P)$(LIB_DIR)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \
org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames RegularClassAndInterfaceFinalFieldTests \
-groups $(TEST_GROUP) \
-excludegroups $(DEFAULT_EXCLUDE); \
$(TEST_STATUS)
</command>
<levels>
<level>sanity</level>
</levels>
<groups>
<group>functional</group>
</groups>
<versions>
<version>15+</version>
</versions>
</test>
</playlist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.openj9.test.hiddenclasses;

/*******************************************************************************
* Copyright (c) 2022, 2022 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
*******************************************************************************/

import jdk.internal.misc.Unsafe;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.testng.annotations.Test;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;

import static org.objectweb.asm.Opcodes.*;

@Test(groups = { "level.sanity" })
public class HiddenClassFinalFieldTests {

static Unsafe unsafe = Unsafe.getUnsafe();

Class<?> makeHiddenClass() throws Exception {
MethodHandles.Lookup lookup = MethodHandles.lookup();

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
FieldVisitor fv;
MethodVisitor mv;
cw.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "org/openj9/test/hiddenclasses/TestHiddenClass", null, "java/lang/Object", null);
{
fv = cw.visitField(ACC_PUBLIC | ACC_STATIC, "modifiableField", "Ljava/lang/String;", null, "old");
fv.visitEnd();
}
{
cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, "finalField", "Ljava/lang/String;", null, "old");
fv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
cw.visitEnd();

return lookup.defineHiddenClass(cw.toByteArray(), true, MethodHandles.Lookup.ClassOption.NESTMATE).lookupClass();
}

/* Final static fields in hidden classes are not modifiable through reflection. */
@Test(expectedExceptions = java.lang.IllegalAccessException.class)
public void test_javaLangReflectFieldSet_finalStaticHiddenClassField() throws Throwable {
Class<?> c = makeHiddenClass();

Object hiddenClassObject = c.getConstructor().newInstance();
Field finalHiddenClassField = hiddenClassObject.getClass().getDeclaredField("finalField");
finalHiddenClassField.setAccessible(true);
finalHiddenClassField.set(hiddenClassObject, "new");
}

/* Make sure non-final fields in hidden classes can still be modified. */
@Test
public void test_javaLangReflectFieldSet_StaticHiddenClassField() throws Throwable {
Class<?> c = makeHiddenClass();

Object hiddenClassObject = c.getConstructor().newInstance();
Field finalHiddenClassField = hiddenClassObject.getClass().getDeclaredField("modifiableField");
finalHiddenClassField.setAccessible(true);
finalHiddenClassField.set(hiddenClassObject, "new");
}

/* Check that Unsafe.staticFieldOffset supports hidden classes. */
@Test
public void test_jdkInternalMiscUnsafe_staticFieldOffset() throws Throwable {
Class<?> c = makeHiddenClass();
Field finalHiddenClassField = c.getDeclaredField("finalField");
unsafe.staticFieldOffset(finalHiddenClassField);
}

/* Check that Unsafe.staticFieldBase supports hidden classes. */
@Test
public void test_jdkInternalMiscUnsafe_staticFieldBase() throws Throwable {
Class<?> c = makeHiddenClass();
Field finalHiddenClassField = c.getDeclaredField("finalField");
unsafe.staticFieldBase(finalHiddenClassField);
}

/* VarHandle.set is not supported for hidden classes. */
@Test(expectedExceptions = java.lang.UnsupportedOperationException.class)
public void test_javaLangInvokeMethodHandles_unreflectVarHandle_static() throws Throwable {
Class<?> c = makeHiddenClass();
Field finalHiddenClassField = c.getDeclaredField("finalField");
finalHiddenClassField.setAccessible(true);
VarHandle finalHiddenClassFieldHandle = MethodHandles.lookup().unreflectVarHandle(finalHiddenClassField);
finalHiddenClassFieldHandle.set("new");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.openj9.test.records;

/*******************************************************************************
* Copyright (c) 2020, 2020 IBM Corp. and others
* Copyright (c) 2020, 2022 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 @@ -129,7 +129,7 @@ public void test_jdkInternalMiscUnsafe_objectFieldOffset() throws Throwable {
unsafe.objectFieldOffset(finalRecordField);
}

/* Check that Unsafe.staticFieldBase supports records. */
/* Check that Unsafe.staticFieldOffset supports records. */
@Test
public void test_jdkInternalMiscUnsafe_staticFieldOffset() throws Throwable {
Field finalRecordField = TestRecord.class.getDeclaredField("finalField");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.openj9.test.regularclassesandinterfaces;

/*******************************************************************************
* Copyright (c) 2022, 2022 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
*******************************************************************************/

import jdk.internal.misc.Unsafe;
import org.testng.annotations.Test;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;

@Test(groups = { "level.sanity" })
public class RegularClassAndInterfaceFinalFieldTests {
static Unsafe unsafe = Unsafe.getUnsafe();

private class TestClass {
static String modifiableField = "old";
static final String finalField = "old";
}

private interface TestInterface {
static final String finalField = "old";
}

/* Final static fields in regular classes are not modifiable through reflection. */
@Test(expectedExceptions = java.lang.IllegalAccessException.class)
public void test_javaLangReflectFieldSet_finalStaticClassField() throws Throwable {
TestClass classObject = new TestClass();
Field finalClassField = classObject.getClass().getDeclaredField("finalField");
finalClassField.setAccessible(true);
finalClassField.set(classObject, "new");
}

/* Make sure non-final fields in regular classes can still be modified. */
@Test
public void test_javaLangReflectFieldSet_staticClassField() throws Throwable {
TestClass classObject = new TestClass();
Field classField = classObject.getClass().getDeclaredField("modifiableField");
classField.setAccessible(true);
classField.set(null, "new");
}

/* Final static fields in interface are not modifiable through reflection. */
@Test(expectedExceptions = java.lang.IllegalAccessException.class)
public void test_javaLangReflectFieldSet_finalStaticInterfaceField() throws Throwable {
Field finalInterfaceField = TestInterface.class.getDeclaredField("finalField");
finalInterfaceField.setAccessible(true);
finalInterfaceField.set(null, "new");
}

/* Check that Unsafe.staticFieldOffset supports regular classes. */
@Test
public void test_jdkInternalMiscUnsafe_staticClassFieldOffset() throws Throwable {
Field finalClassField = TestClass.class.getDeclaredField("finalField");
unsafe.staticFieldOffset(finalClassField);
}

/* Check that Unsafe.staticFieldBase supports regular classes. */
@Test
public void test_jdkInternalMiscUnsafe_staticClassFieldBase() throws Throwable {
Field finalClassField = TestClass.class.getDeclaredField("finalField");
unsafe.staticFieldBase(finalClassField);
}

/* Check that Unsafe.staticFieldOffset supports interfaces. */
@Test
public void test_jdkInternalMiscUnsafe_staticInterfaceFieldOffset() throws Throwable {
Field finalInterfaceField = TestInterface.class.getDeclaredField("finalField");
unsafe.staticFieldOffset(finalInterfaceField);
}

/* Check that Unsafe.staticFieldBase supports interfaces. */
@Test
public void test_jdkInternalMiscUnsafe_staticInterfaceFieldBase() throws Throwable {
Field finalInterfaceField = TestInterface.class.getDeclaredField("finalField");
unsafe.staticFieldBase(finalInterfaceField);
}

/* VarHandle.set is not supported for final field in regular classes. */
@Test(expectedExceptions = java.lang.UnsupportedOperationException.class)
public void test_javaLangInvokeMethodHandles_unreflectVarHandle_class_static() throws Throwable {
Field finalClassField = TestClass.class.getDeclaredField("finalField");
finalClassField.setAccessible(true);
VarHandle finalClassFieldHandle = MethodHandles.lookup().unreflectVarHandle(finalClassField);
finalClassFieldHandle.set("new");
}

/* VarHandle.set is not supported for final field in interfaces. */
@Test(expectedExceptions = java.lang.UnsupportedOperationException.class)
public void test_javaLangInvokeMethodHandles_unreflectVarHandle_interface_static() throws Throwable {
Field finalInterfaceField = TestInterface.class.getDeclaredField("finalField");
finalInterfaceField.setAccessible(true);
VarHandle finalInterfaceFieldHandle = MethodHandles.lookup().unreflectVarHandle(finalInterfaceField);
finalInterfaceFieldHandle.set("new");
}
}
12 changes: 11 additions & 1 deletion test/functional/Java15andUp/testng.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
Copyright (c) 2020, 2020 IBM Corp. and others
Copyright (c) 2020, 2022 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 @@ -36,4 +36,14 @@
<class name="org.openj9.test.records.RecordFinalFieldTests"/>
</classes>
</test>
<test name="Jep371Tests">
<classes>
<class name="org.openj9.test.hiddenclasses.HiddenClassFinalFieldTests"/>
</classes>
</test>
<test name="RegularClassAndInterfaceFinalFieldTests">
<classes>
<class name="org.openj9.test.regularclassesandinterfaces.RegularClassAndInterfaceFinalFieldTests"/>
</classes>
</test>
</suite>

0 comments on commit 91e41ae

Please sign in to comment.