Skip to content

Commit

Permalink
Merge pull request #11097 from hzongaro/unresolved-value-types-tests
Browse files Browse the repository at this point in the history
 Add tests for unresolved value types
  • Loading branch information
tajila committed Nov 24, 2020
2 parents a3c957c + fbba191 commit 341fc39
Show file tree
Hide file tree
Showing 5 changed files with 635 additions and 11 deletions.
4 changes: 2 additions & 2 deletions test/functional/Valhalla/build.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>

<!--
Copyright (c) 2017, 2019 IBM Corp. and others
Copyright (c) 2017, 2020 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 @@ -58,7 +58,7 @@
<classpath>
<pathelement location="${LIB_DIR}/testng.jar"/>
<pathelement location="${LIB_DIR}/jcommander.jar"/>
<pathelement location="${LIB_DIR}/asm-all.jar"/>
<pathelement location="${LIB_DIR}/asm.jar"/>
</classpath>
</javac>
</target>
Expand Down
4 changes: 2 additions & 2 deletions test/functional/Valhalla/playlist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
-Xint \
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED \
-XX:+EnableValhalla \
-cp $(Q)$(LIB_DIR)$(D)asm-all.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)ValhallaTests.jar$(Q) \
-cp $(Q)$(LIB_DIR)$(D)asm.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)ValhallaTests.jar$(Q) \
org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames ValueTypeTests \
-groups $(TEST_GROUP) \
-excludegroups $(DEFAULT_EXCLUDE); \
Expand Down Expand Up @@ -69,7 +69,7 @@
-Xverify:none \
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED \
-XX:+EnableValhalla \
-cp $(Q)$(LIB_DIR)$(D)asm-all.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)ValhallaTests.jar$(Q) \
-cp $(Q)$(LIB_DIR)$(D)asm.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)ValhallaTests.jar$(Q) \
org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames ValueTypeTestsJIT \
-groups $(TEST_GROUP) \
-excludegroups $(DEFAULT_EXCLUDE); \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,132 @@ public class ValueTypeGenerator extends ClassLoader {
static {
generator = new ValueTypeGenerator();
}

private static byte[] generateClass(String className, String[] fields, boolean isVerifiable, boolean isRef) {

private static class ClassConfiguration {
private String name;
private String[] fields;
private String nestHost;
private boolean isVerifiable;
private boolean isReference;

/**
* @see setAccessedContainer
*/
private ClassConfiguration accessedContainer;

/**
* @see setValueClassUsedInCode
*/
private ClassConfiguration valueClassUsedInCode;

public ClassConfiguration(String name) {
this.name = name;
}

public ClassConfiguration(String name, String[] fields) {
this.name = name;
this.fields = fields;
}

public String getName() {
return name;
}

public String[] getFields() {
return fields;
}

public String getNestHost() {
return nestHost;
}

public void setNestHost(String nestHost) {
this.nestHost = nestHost;
}

public void setIsReference(boolean isReference) {
this.isReference = isReference;
}

public boolean isReference() {
return isReference;
}

public void setIsVerifiable(boolean isVerifiable) {
this.isVerifiable = isVerifiable;
}

public boolean isVerifiable() {
return this.isVerifiable;
}

/**
* This method specifies a reference class - whose fields are expected to be of
* value types - an instance of which will be an argument to the
* {@code testUnresolvedValueTypePutField} and {@code testUnresolvedValueTypeGetField}
* methods that will be generated for the current class. Those methods will perform
* {@code PUTFIELD} or {@code GETFIELD} operations, respectively, on the fields of
* the {@code accessedContainer} instance.
*
* The intention is to test delaying resolution of the fields and their types,
* particularly its effect on code generated by the JIT compiler.
*/
public void setAccessedContainer(ClassConfiguration accessedContainer) {
this.accessedContainer = accessedContainer;
}

/**
* @see setAccessedContainer
*/
public ClassConfiguration getAccessedContainer() {
return accessedContainer;
}

/**
* This method specifies a value type class that will be used in code generated
* for the {@code testUnresolvedValueTypeDefaultValue} and
* {@code testUnresolvedValueTypeWithField} methods of the current class.
* The former will conditionally perform a {@code DEFAULTVALUE} operation on the
* value type and the latter will conditionally perform a series of
* {@code WITHFIELD} operations on the fields of an instance of the value type.
* The instance should be passed to {@code testUnresolvedValueTypeWithField} via an
* argument of type {@link java.lang.Object}.
*
* The value type class will be declared to be a {@code NestMember} of the current
* class, and must in turn declare the current class to be its {@code NestHost}.
*
* The intention is to test delayed resolution of the value type class, particularly
* its effect on code generated by the JIT compiler.
*/
public void setValueClassUsedInCode(ClassConfiguration valueClassUsedInCode) {
this.valueClassUsedInCode = valueClassUsedInCode;
}

/**
* @see setValueClassUsedInCode
*/
public ClassConfiguration getValueClassUsedInCode() {
return valueClassUsedInCode;
}
}

private static byte[] generateClass(ClassConfiguration config) {
String className = config.getName();
String[] fields = config.getFields();

String nestHost = config.getNestHost();

ClassConfiguration valueClassConfig = config.getValueClassUsedInCode();
String valueUsedInCode = (valueClassConfig != null) ? valueClassConfig.getName() : null;
String[] valueFields = (valueClassConfig != null) ? valueClassConfig.getFields() : null;

ClassConfiguration containerClassConfig = config.getAccessedContainer();
String containerUsedInCode = (containerClassConfig != null) ? containerClassConfig.getName() : null;
String[] containerFields = (containerClassConfig != null) ? containerClassConfig.getFields() : null;

boolean isVerifiable = config.isVerifiable();
boolean isRef = config.isReference();

ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
Expand All @@ -60,6 +184,14 @@ private static byte[] generateClass(String className, String[] fields, boolean i
}

cw.visitSource(className + ".java", null);

if (nestHost != null) {
cw.visitNestHost(nestHost);
}

if (valueUsedInCode != null) {
cw.visitNestMember(valueUsedInCode);
}

int makeMaxLocal = 0;
String makeValueSig = "";
Expand Down Expand Up @@ -110,6 +242,16 @@ private static byte[] generateClass(String className, String[] fields, boolean i
testMonitorExitOnObject(cw, className, fields);
testMonitorEnterAndExitWithRefType(cw, className, fields);
testCheckCastRefClassOnNull(cw, className, fields);
if (valueUsedInCode != null) {
testUnresolvedValueTypeDefaultValue(cw, className, valueUsedInCode);
if (valueFields != null) {
testUnresolvedValueTypeWithField(cw, className, valueUsedInCode, valueFields);
}
}
if (containerFields != null) {
testUnresolvedValueTypePutField(cw, className, containerUsedInCode, containerFields);
testUnresolvedValueTypeGetField(cw, className, containerUsedInCode, containerFields);
}
} else {
makeValue(cw, className, makeValueSig, fields, makeMaxLocal);
makeValueTypeDefaultValue(cw, className, makeValueSig, fields, makeMaxLocal);
Expand Down Expand Up @@ -380,6 +522,101 @@ private static void testCheckCastRefClassOnNull(ClassWriter cw, String className
mv.visitEnd();
}

private static void testUnresolvedValueTypeDefaultValue(ClassWriter cw, String className, String valueUsedInCode) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testUnresolvedValueTypeDefaultValue", "(I)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 0);
Label falseLabel = new Label();
Label endLabel = new Label();
mv.visitJumpInsn(IFEQ, falseLabel);
mv.visitTypeInsn(DEFAULTVALUE, getSigFromSimpleName(valueUsedInCode, false));
mv.visitJumpInsn(GOTO, endLabel);
mv.visitLabel(falseLabel);
mv.visitInsn(ACONST_NULL);
mv.visitLabel(endLabel);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}

private static void testUnresolvedValueTypeWithField(ClassWriter cw, String className, String valueUsedInCode, String[] valueFields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testUnresolvedValueTypeWithField", "(ILjava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 0);
Label falseLabel = new Label();
Label endLabel = new Label();
mv.visitJumpInsn(IFEQ, falseLabel);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, valueUsedInCode);
for (int i = 0; i < valueFields.length; i++) {
String[] nameAndSig = valueFields[i].split(":");
mv.visitLdcInsn(Integer.valueOf(i+1));
mv.visitFieldInsn(WITHFIELD, valueUsedInCode, nameAndSig[0], nameAndSig[1]);
}
mv.visitJumpInsn(GOTO, endLabel);
mv.visitLabel(falseLabel);
mv.visitInsn(ACONST_NULL);
mv.visitLabel(endLabel);
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}

private static void testUnresolvedValueTypeGetField(ClassWriter cw, String className, String containerClassName, String[] containerFields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testUnresolvedValueTypeGetField", "(IL"+containerClassName+";)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 0);
int fieldCount = containerFields.length;
Label endLabel = new Label();
Label defaultLabel = new Label();
Label[] caseLabels = new Label[fieldCount];
for (int i = 0; i < fieldCount; i++) {
caseLabels[i] = new Label();
}
mv.visitTableSwitchInsn(0, fieldCount-1, defaultLabel, caseLabels);
for (int i = 0; i < fieldCount; i++) {
String nameAndSigValue[] = containerFields[i].split(":");
mv.visitLabel(caseLabels[i]);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(GETFIELD, containerClassName, nameAndSigValue[0], nameAndSigValue[1]);
mv.visitJumpInsn(GOTO, endLabel);
}
mv.visitLabel(defaultLabel);
mv.visitInsn(ACONST_NULL);
mv.visitLabel(endLabel);
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 2);
mv.visitEnd();
}

private static void testUnresolvedValueTypePutField(ClassWriter cw, String className, String containerClassName, String[] containerFields) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testUnresolvedValueTypePutField", "(IL"+containerClassName+";Ljava/lang/Object;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 0);
int fieldCount = containerFields.length;
Label endLabel = new Label();
Label defaultLabel = new Label();
Label[] caseLabels = new Label[fieldCount];
for (int i = 0; i < fieldCount; i++) {
caseLabels[i] = new Label();
}
mv.visitTableSwitchInsn(0, fieldCount-1, defaultLabel, caseLabels);
for (int i = 0; i < fieldCount; i++) {
String nameAndSigValue[] = containerFields[i].split(":");
mv.visitLabel(caseLabels[i]);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, nameAndSigValue[1]);
mv.visitFieldInsn(PUTFIELD, containerClassName, nameAndSigValue[0], nameAndSigValue[1]);
mv.visitJumpInsn(GOTO, endLabel);
}
mv.visitLabel(defaultLabel);
mv.visitLabel(endLabel);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 3);
mv.visitEnd();
}

private static void makeRefDefaultValue(ClassWriter cw, String className, String makeValueSig, String[] fields, int makeMaxLocal) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "makeRefDefaultValue", "()L" + className + ";", null, null);
mv.visitCode();
Expand Down Expand Up @@ -921,17 +1158,64 @@ public static void generateClassFile(String name, byte[] bytes) {
}

public static Class<?> generateValueClass(String name, String[] fields) throws Throwable {
byte[] bytes = generateClass(name, fields, false, false);
return generateValueClass(name, fields, null);
}

public static Class<?> generateValueClass(String name, String[] fields, String nestHost) throws Throwable {
ClassConfiguration classConfig = new ClassConfiguration(name, fields);

if (nestHost != null) {
classConfig.setNestHost(nestHost);
}

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

public static Class<?> generateVerifiableValueClass(String name, String[] fields) throws Throwable {
byte[] bytes = generateClass(name, fields, true, false);
ClassConfiguration classConfig = new ClassConfiguration(name, fields);
classConfig.setIsVerifiable(true);

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

public static Class<?> generateRefClass(String name, String[] fields) throws Throwable {
byte[] bytes = generateClass(name, fields, false, true);
ClassConfiguration classConfig = new ClassConfiguration(name, fields);
classConfig.setIsReference(true);

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

public static Class<?> generateRefClass(String name, String[] fields, String valueUsedInCode) throws Throwable {
ClassConfiguration classConfig = new ClassConfiguration(name, fields);
ClassConfiguration valueClassConfig = new ClassConfiguration(valueUsedInCode);
classConfig.setValueClassUsedInCode(valueClassConfig);
classConfig.setIsReference(true);

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

public static Class<?> generateHostRefClass(String name, String[] fields, String valueUsedInCode, String[] valueFields) throws Throwable {
ClassConfiguration classConfig = new ClassConfiguration(name, fields);
ClassConfiguration valueClassConfig = new ClassConfiguration(valueUsedInCode, valueFields);
classConfig.setValueClassUsedInCode(valueClassConfig);
classConfig.setIsReference(true);

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

public static Class<?> generateRefClass(String name, String[] fields, String containerClassName, String[] containerFields) throws Throwable {
ClassConfiguration classConfig = new ClassConfiguration(name, fields);
ClassConfiguration containerClassConfig = new ClassConfiguration(containerClassName, containerFields);
containerClassConfig.setIsReference(true);
classConfig.setAccessedContainer(containerClassConfig);
classConfig.setIsReference(true);

byte[] bytes = generateClass(classConfig);
return generator.defineClass(name, bytes, 0, bytes.length);
}

Expand Down
Loading

0 comments on commit 341fc39

Please sign in to comment.