diff --git a/test/functional/Valhalla/build.xml b/test/functional/Valhalla/build.xml index d5dd95e978f..5f9903e48c4 100644 --- a/test/functional/Valhalla/build.xml +++ b/test/functional/Valhalla/build.xml @@ -62,6 +62,7 @@ + diff --git a/test/functional/Valhalla/playlist.xml b/test/functional/Valhalla/playlist.xml index 51149731acb..ea13cb2ff82 100644 --- a/test/functional/Valhalla/playlist.xml +++ b/test/functional/Valhalla/playlist.xml @@ -36,7 +36,7 @@ -Xnocompressedrefs -Xgcpolicy:gencon $(JAVA_COMMAND) $(JVM_OPTIONS) \ - -Xint \ + -Xint --enable-preview \ --add-opens java.base/jdk.internal.misc=ALL-UNNAMED \ --add-exports java.base/jdk.internal.value=ALL-UNNAMED \ -cp $(Q)$(LIB_DIR)$(D)asm.jar$(P)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)ValhallaTests.jar$(Q) \ diff --git a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeGenerator.java b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeGenerator.java index 9ed294f6c83..2befcf1eb17 100644 --- a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeGenerator.java +++ b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeGenerator.java @@ -30,61 +30,55 @@ public class ValhallaAttributeGenerator extends ClassLoader { public static Class generateClassWithTwoPreloadAttributes(String name, String[] classList1, String[] classList2) throws Throwable { byte[] bytes = generateClass(name, ValhallaUtils.ACC_IDENTITY, new Attribute[] { - new PreloadAttribute(classList1), - new PreloadAttribute(classList2)}); + new ValhallaUtils.PreloadAttribute(classList1), + new ValhallaUtils.PreloadAttribute(classList2)}); return generator.defineClass(name, bytes, 0, bytes.length); } public static Class generateClassWithPreloadAttribute(String name, String[] classList) throws Throwable { - byte[] bytes = generateClass(name, ValhallaUtils.ACC_IDENTITY, new Attribute[] {new PreloadAttribute(classList)}); + byte[] bytes = generateClass(name, ValhallaUtils.ACC_IDENTITY, new Attribute[] {new ValhallaUtils.PreloadAttribute(classList)}); return generator.defineClass(name, bytes, 0, bytes.length); } public static Class generateClassWithTwoImplicitCreationAttributes(String name) throws Throwable { byte[] bytes = generateClass(name, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute(), new ImplicitCreationAttribute()}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute(), new ValhallaUtils.ImplicitCreationAttribute()}); return generator.defineClass(name, bytes, 0, bytes.length); } public static Class generateNonValueTypeClassWithImplicitCreationAttribute(String name) throws Throwable { - byte[] bytes = generateClass(name, ValhallaUtils.ACC_IDENTITY, new Attribute[] {new ImplicitCreationAttribute()}); - return generator.defineClass(name, bytes, 0, bytes.length); - } - - public static Class generateValidClassWithImplicitCreationAttribute(String name) throws Throwable { - byte[] bytes = generateClass(name, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute()}); + byte[] bytes = generateClass(name, ValhallaUtils.ACC_IDENTITY, new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute()}); return generator.defineClass(name, bytes, 0, bytes.length); } public static Class generateFieldWithMultipleNullRestrictedAttributes(String className, String fieldClassName) throws Throwable { /* Generate field class - value class with ImplicitCreation attribute and ACC_DEFAULT flag set */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute()}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute()}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); /* Generate class with field and multiple NullRestricted attributes */ byte[] classBytes = generateIdentityClassWithField(className, 0, - "field", fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute(), new NullRestrictedAttribute()}); + "field", fieldClass.descriptorString(), new Attribute[]{new ValhallaUtils.NullRestrictedAttribute(), new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } public static Class generateNullRestrictedAttributeInPrimitiveField(String className) throws Throwable { /* Generate class with primitive field and a NullRestricted attribute */ byte[] classBytes = generateIdentityClassWithField(className, 0, - "field", "I", new Attribute[]{new NullRestrictedAttribute()}); + "field", "I", new Attribute[]{new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } public static Class generateNullRestrictedAttributeInArrayField(String className, String fieldClassName) throws Throwable { /* Generate field class - value class with ImplicitCreation attribute and ACC_DEFAULT flag set. */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute()}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute()}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); /* Generate class field field that is an array with a NullRestricted attribute */ byte[] classBytes = generateIdentityClassWithField(className, 0, - "field", "[" + fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute()}); + "field", "[" + fieldClass.descriptorString(), new Attribute[]{new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } @@ -93,7 +87,7 @@ public static Class generatePutStaticNullToNullRestrictedField(String classNa /* Generate field class - value class with ImplicitCreation attribute and ACC_DEFAULT flag set. */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_PUBLIC + ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute()}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute()}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); ClassWriter classWriter = new ClassWriter(0); @@ -101,7 +95,7 @@ public static Class generatePutStaticNullToNullRestrictedField(String classNa /* static field of previously generated field class with NullRestrictd attribute */ FieldVisitor fieldVisitor = classWriter.visitField(ACC_PUBLIC + ACC_STATIC, fieldName, fieldClass.descriptorString(), null, null); - fieldVisitor.visitAttribute(new NullRestrictedAttribute()); + fieldVisitor.visitAttribute(new ValhallaUtils.NullRestrictedAttribute()); /* assign field to null in */ MethodVisitor mvClinit = classWriter.visitMethod(ACC_STATIC, "", "()V", null, null); @@ -131,7 +125,7 @@ public static Class generatePutFieldNullToNullRestrictedField(String classNam /* Generate field class - value class with ImplicitCreation attribute and ACC_DEFAULT flag set. */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_PUBLIC + ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute()}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute()}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); ClassWriter classWriter = new ClassWriter(0); @@ -139,7 +133,7 @@ public static Class generatePutFieldNullToNullRestrictedField(String classNam /* instance field of previously generated field class with NullRestrictd attribute */ FieldVisitor fieldVisitor = classWriter.visitField(ACC_PUBLIC, fieldName, fieldClass.descriptorString(), null, null); - fieldVisitor.visitAttribute(new NullRestrictedAttribute()); + fieldVisitor.visitAttribute(new ValhallaUtils.NullRestrictedAttribute()); /* assign field to null in */ MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, "", "()V", null, null); @@ -165,7 +159,7 @@ public static Class generateNullRestrictedAttributeInIdentityClass(boolean is /* Generate class with field that is an identity class with a NullRestricted attribute */ byte[] classBytes = generateIdentityClassWithField(className, isStatic ? ACC_STATIC : 0, - "field", fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute()}); + "field", fieldClass.descriptorString(), new Attribute[]{new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } @@ -176,19 +170,19 @@ public static Class generateNullRestrictedAttributeInValueClassWithoutIC(bool /* Generate class with field that has a NullRestricted attribute */ byte[] classBytes = generateIdentityClassWithField(className, isStatic ? ACC_STATIC : 0, - "field", fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute()}); + "field", fieldClass.descriptorString(), new Attribute[]{new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } public static Class generateNullRestrictedFieldWhereICHasNoDefaultFlag(boolean isStatic, String className, String fieldClassName) throws Throwable { /* Generate field class - value type with ImplicitCreation attribute, no flags */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute(0)}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute(0)}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); /* Generate class with field with NullRestricted attribute */ byte[] classBytes = generateIdentityClassWithField(className, isStatic ? ACC_STATIC : 0, - "field", fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute()}); + "field", fieldClass.descriptorString(), new Attribute[]{new ValhallaUtils.NullRestrictedAttribute()}); return generator.defineClass(className, classBytes, 0, classBytes.length); } @@ -197,7 +191,7 @@ public static Class generateWithFieldStoreNullToNullRestrictedField(String cl /* Generate field class - value class with ImplicitCreation attribute and ACC_DEFAULT flag set. */ byte[] fieldClassBytes = generateClass(fieldClassName, ACC_PUBLIC + ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, - new Attribute[] {new ImplicitCreationAttribute(ValhallaUtils.ACC_DEFAULT)}); + new Attribute[] {new ValhallaUtils.ImplicitCreationAttribute(ValhallaUtils.ACC_DEFAULT)}); Class fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length); ClassWriter classWriter = new ClassWriter(0); @@ -205,7 +199,7 @@ public static Class generateWithFieldStoreNullToNullRestrictedField(String cl /* instance field of previously generated field class with NullRestrictd attribute */ FieldVisitor fieldVisitor = classWriter.visitField(ACC_PUBLIC, fieldName, fieldClass.descriptorString(), null, null); - fieldVisitor.visitAttribute(new NullRestrictedAttribute()); + fieldVisitor.visitAttribute(new ValhallaUtils.NullRestrictedAttribute()); MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, "", "()V", null, null); mvInit.visitCode(); @@ -269,93 +263,4 @@ public static byte[] generateIdentityClassWithField(String name, int fieldFlags, classWriter.visitEnd(); return classWriter.toByteArray(); } - - final static class PreloadAttribute extends Attribute { - private final String[] classes; - - public PreloadAttribute(String[] classes) { - super("Preload"); - this.classes = classes; - } - - public Label[] getLabels() { - return null; - } - - public boolean isCodeAttribute() { - return false; - } - - public boolean isUnknown() { - return true; - } - - public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { - ByteVector b = new ByteVector(); - - b.putShort(classes.length); - - int cpIndex; - for (int i = 0; i < classes.length; i++) { - cpIndex = cw.newClass(classes[i].replace('.', '/')); - b.putShort(cpIndex); - } - return b; - } - } - - final static class ImplicitCreationAttribute extends Attribute { - private final int flags; - - public ImplicitCreationAttribute() { - super("ImplicitCreation"); - /* this is the default flag generated by the compiler for the implicit modifier. */ - this.flags = ValhallaUtils.ACC_DEFAULT; - } - - public ImplicitCreationAttribute(int flags) { - super("ImplicitCreation"); - this.flags = flags; - } - - public Label[] getLabels() { - return null; - } - - public boolean isCodeAttribute() { - return false; - } - - public boolean isUnknown() { - return true; - } - - public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { - ByteVector b = new ByteVector(); - b.putShort(flags); - return b; - } - } - - final static class NullRestrictedAttribute extends Attribute { - public NullRestrictedAttribute() { - super("NullRestricted"); - } - - public Label[] getLabels() { - return null; - } - - public boolean isCodeAttribute() { - return false; - } - - public boolean isUnknown() { - return true; - } - - public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { - return new ByteVector(); - } - } } diff --git a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeTests.java b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeTests.java index fd9049b2e9c..fd1758e5b11 100644 --- a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeTests.java +++ b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaAttributeTests.java @@ -77,14 +77,6 @@ static public void testNonValueTypeClassWithImplicitCreationAttribute() throws T ValhallaAttributeGenerator.generateNonValueTypeClassWithImplicitCreationAttribute("NonValueTypeImplicitCreationAttribute"); } - /* ImplicitCreation smoke test. The attribute doesn't do anything in the vm right now, make sure - * it passes class validation. - */ - @Test - static public void testValueTypeClassWithImplicitCreationAttribute() throws Throwable { - ValhallaAttributeGenerator.generateValidClassWithImplicitCreationAttribute("ValueTypeClassWithImplicitCreationAttribute"); - } - /* There must be no more than one NullRestricted attribute in the attributes table of a field_info structure */ @Test(expectedExceptions = java.lang.ClassFormatError.class, expectedExceptionsMessageRegExp = ".*Multiple NullRestricted attributes present.*") static public void testMultipleNullRestrictedAttributes() throws Throwable { diff --git a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaUtils.java b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaUtils.java index 73d3088493e..92db7e84409 100644 --- a/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaUtils.java +++ b/test/functional/Valhalla/src/org/openj9/test/lworld/ValhallaUtils.java @@ -21,6 +21,8 @@ *******************************************************************************/ package org.openj9.test.lworld; +import org.objectweb.asm.*; + public class ValhallaUtils { /** * Currently value type is built on JDK22, so use java file major version 66 for now. @@ -40,4 +42,92 @@ public class ValhallaUtils { static final int ACC_DEFAULT = 0x1; static final int ACC_NON_ATOMIC = 0x2; + final static class PreloadAttribute extends Attribute { + private final String[] classes; + + public PreloadAttribute(String[] classes) { + super("Preload"); + this.classes = classes; + } + + public Label[] getLabels() { + return null; + } + + public boolean isCodeAttribute() { + return false; + } + + public boolean isUnknown() { + return true; + } + + public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { + ByteVector b = new ByteVector(); + + b.putShort(classes.length); + + int cpIndex; + for (int i = 0; i < classes.length; i++) { + cpIndex = cw.newClass(classes[i].replace('.', '/')); + b.putShort(cpIndex); + } + return b; + } + } + + final static class ImplicitCreationAttribute extends Attribute { + private final int flags; + + public ImplicitCreationAttribute() { + super("ImplicitCreation"); + /* this is the default flag generated by the compiler for the implicit modifier. */ + this.flags = ValhallaUtils.ACC_DEFAULT; + } + + public ImplicitCreationAttribute(int flags) { + super("ImplicitCreation"); + this.flags = flags; + } + + public Label[] getLabels() { + return null; + } + + public boolean isCodeAttribute() { + return false; + } + + public boolean isUnknown() { + return true; + } + + public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { + ByteVector b = new ByteVector(); + b.putShort(flags); + return b; + } + } + + final static class NullRestrictedAttribute extends Attribute { + public NullRestrictedAttribute() { + super("NullRestricted"); + } + + public Label[] getLabels() { + return null; + } + + public boolean isCodeAttribute() { + return false; + } + + public boolean isUnknown() { + return true; + } + + public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) { + return new ByteVector(); + } + } } diff --git a/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeGenerator.java b/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeGenerator.java index 7f07aeb7df5..4cd6af6f72e 100644 --- a/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeGenerator.java +++ b/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeGenerator.java @@ -34,16 +34,21 @@ public class ValueTypeGenerator extends ClassLoader { private static class ClassConfiguration { private String name; + private String superName; private String[] fields; private boolean isReference; private boolean hasNonStaticSynchronizedMethods; + private int extraClassFlags; + private ClassConfiguration accessedContainer; public ClassConfiguration(String name) { this.name = name; + this.superName = "java/lang/Object"; } public ClassConfiguration(String name, String[] fields) { this.name = name; + this.superName = "java/lang/Object"; this.fields = fields; } @@ -72,6 +77,44 @@ public void setHasNonStaticSynchronizedMethods(boolean hasNonStaticSynchronizedM public boolean hasNonStaticSynchronizedMethods() { return hasNonStaticSynchronizedMethods; } + + public void setSuperClassName(String superName) { + this.superName = superName; + } + + public String getSuperName() { + return superName; + } + + public void setExtraClassFlags(int extraClassFlags) { + this.extraClassFlags = extraClassFlags; + } + + public int getExtraClassFlags() { + return extraClassFlags; + } + + /** + * 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; + } } public static Class generateRefClass(String name) throws Throwable { @@ -82,6 +125,25 @@ public static Class generateRefClass(String name) throws Throwable { return generator.defineClass(name, bytes, 0, bytes.length); } + public static Class generateRefClass(String name, String[] fields) throws Throwable { + 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 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); + } + public static Class generateValueClass(String name) throws Throwable { ClassConfiguration classConfig = new ClassConfiguration(name); @@ -89,8 +151,23 @@ public static Class generateValueClass(String name) throws Throwable { return generator.defineClass(name, bytes, 0, bytes.length); } - public static Class generateIllegalValueClassWithSychMethods(String name) throws Throwable { - ClassConfiguration classConfig = new ClassConfiguration(name); + public static Class generateValueClass(String name, String[] fields) throws Throwable { + ClassConfiguration classConfig = new ClassConfiguration(name, fields); + + byte[] bytes = generateClass(classConfig); + return generator.defineClass(name, bytes, 0, bytes.length); + } + + public static Class generateValueClass(String name, String superClassName, String[] fields, int extraFlags) throws Throwable { + ClassConfiguration classConfig = new ClassConfiguration(name, fields); + classConfig.setSuperClassName(superClassName); + classConfig.setExtraClassFlags(extraFlags); + byte[] bytes = generateClass(classConfig); + return generator.defineClass(name, bytes, 0, bytes.length); + } + + public static Class generateIllegalValueClassWithSynchMethods(String name, String[] fields) throws Throwable { + ClassConfiguration classConfig = new ClassConfiguration(name, fields); classConfig.setHasNonStaticSynchronizedMethods(true); byte[] bytes = generateClass(classConfig); return generator.defineClass(name, bytes, 0, bytes.length); @@ -98,21 +175,53 @@ public static Class generateIllegalValueClassWithSychMethods(String name) thr private static byte[] generateClass(ClassConfiguration config) { String className = config.getName(); + String superName = config.getSuperName(); String[] fields = config.getFields(); boolean isRef = config.isReference(); boolean addSyncMethods = config.hasNonStaticSynchronizedMethods(); + int extraClassFlags = config.getExtraClassFlags(); + + ClassConfiguration containerClassConfig = config.getAccessedContainer(); + String containerUsedInCode = (containerClassConfig != null) ? containerClassConfig.getName() : null; + String[] containerFields = (containerClassConfig != null) ? containerClassConfig.getFields() : null; + ClassWriter cw = new ClassWriter(0); - int classFlags = ACC_PUBLIC + ACC_FINAL + (isRef? ValhallaUtils.ACC_IDENTITY : ValhallaUtils.ACC_VALUE_TYPE); - cw.visit(ValhallaUtils.CLASS_FILE_MAJOR_VERSION, classFlags, className, null, "java/lang/Object", null); + int classFlags = ACC_PUBLIC + ACC_FINAL + (isRef? ValhallaUtils.ACC_IDENTITY : ValhallaUtils.ACC_VALUE_TYPE) + extraClassFlags; + cw.visit(ValhallaUtils.CLASS_FILE_MAJOR_VERSION, classFlags, + className, null, superName, null); + int makeMaxLocal = 0; + String makeValueSig = ""; + String makeValueGenericSig = ""; if (null != fields) { for (String s : fields) { String nameAndSigValue[] = s.split(":"); - final int fieldModifiers = ACC_PUBLIC; + int fieldModifiers = ACC_PUBLIC; + if ((nameAndSigValue.length > 2) && nameAndSigValue[2].equals("static")) { + fieldModifiers += ACC_STATIC; + } else if (!isRef) { + fieldModifiers += ACC_FINAL; + } FieldVisitor fv = cw.visitField(fieldModifiers, nameAndSigValue[0], nameAndSigValue[1], null, null); + if ((nameAndSigValue.length > 3) && nameAndSigValue[3].equals("NR")) { + fv.visitAttribute(new ValhallaUtils.NullRestrictedAttribute()); + } fv.visitEnd(); + + if ((nameAndSigValue.length <= 2) || !nameAndSigValue[2].equals("static")) { + makeValueSig += nameAndSigValue[1]; + makeValueGenericSig += "Ljava/lang/Object;"; + if (nameAndSigValue[1].equals("J") || nameAndSigValue[1].equals("D")) { + makeMaxLocal += 2; + } else { + makeMaxLocal += 1; + } + + generateGetter(cw, nameAndSigValue, className); + generateGetterGeneric(cw, nameAndSigValue, className); + } } } @@ -121,15 +230,39 @@ private static byte[] generateClass(ClassConfiguration config) { addMakeRef(cw, className); addTestMonitorEnterAndExitWithRefType(cw); addTestMonitorExitOnObject(cw); + addTestCheckCastRefClassOnNull(cw, className, fields); + if (containerFields != null) { + testUnresolvedValueTypePutField(cw, className, containerUsedInCode, containerFields); + testUnresolvedValueTypeGetField(cw, className, containerUsedInCode, containerFields); + } + } else { + /* make value classes eligble to be nullrestricted */ + cw.visitAttribute(new ValhallaUtils.ImplicitCreationAttribute()); + addTestCheckCastValueTypeOnNull(cw, className, fields); + addTestCheckCastValueTypeOnNonNullType(cw, className, fields); + if (null != fields) { + makeValue(cw, className, makeValueSig, fields, makeMaxLocal); + makeGeneric(cw, className, "makeValueGeneric", "makeValue", makeValueSig, makeValueGenericSig, fields, makeMaxLocal, isRef); + makeValueTypeDefaultValue(cw, className, makeValueSig, fields, makeMaxLocal, isRef); + } } + addStaticMethod(cw); addStaticSynchronizedMethods(cw); + addTestCheckCastOnInvalidClass(cw); if (addSyncMethods) { addSynchronizedMethods(cw); } - return cw.toByteArray(); } + private static void addStaticMethod(ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "staticMethod", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + private static void addInit(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); @@ -201,4 +334,298 @@ private static void addTestMonitorExitOnObject(ClassWriter cw) { mv.visitMaxs(1, 1); mv.visitEnd(); } + + private static void addTestCheckCastRefClassOnNull(ClassWriter cw, String className, String[] fields) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testCheckCastRefClassOnNull", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitInsn(ACONST_NULL); + mv.visitTypeInsn(CHECKCAST, className); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + + // TODO remove use of aconst_init + private static void addTestCheckCastValueTypeOnNonNullType(ClassWriter cw, String className, String[] fields) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testCheckCastValueTypeOnNonNullType", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitTypeInsn(ValhallaUtils.ACONST_INIT, "L" + className + ";"); + mv.visitTypeInsn(CHECKCAST, className); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + + private static void addTestCheckCastValueTypeOnNull(ClassWriter cw, String className, String[] fields) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testCheckCastValueTypeOnNull", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitInsn(ACONST_NULL); + mv.visitTypeInsn(CHECKCAST, "L" + className + ";"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + + private static void addTestCheckCastOnInvalidClass(ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "testCheckCastOnInvalidClass", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitInsn(ACONST_NULL); + mv.visitTypeInsn(CHECKCAST, "ClassDoesNotExist"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + + // TODO remove use of aconst_init and withfield + private static void makeValue(ClassWriter cw, String valueName, String makeValueSig, String[] fields, int makeMaxLocal) { + boolean doubleDetected = false; + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "makeValue", "(" + makeValueSig + ")" + "L" + valueName + ";", null, null); + mv.visitCode(); + mv.visitTypeInsn(ValhallaUtils.ACONST_INIT, "L" + valueName + ";"); + for (int i = 0, count = 0; i < fields.length; i++) { + String nameAndSig[] = fields[i].split(":"); + if ((nameAndSig.length < 3) || !(nameAndSig[2].equals("static"))) { + switch (nameAndSig[1]) { + case "D": + mv.visitVarInsn(DLOAD, count); + doubleDetected = true; + count += 2; + break; + case "I": + case "Z": + case "B": + case "C": + case "S": + mv.visitVarInsn(ILOAD, count); + count++; + break; + case "F": + mv.visitVarInsn(FLOAD, count); + count++; + break; + case "J": + mv.visitVarInsn(LLOAD, count); + doubleDetected = true; + count += 2; + break; + default: + mv.visitVarInsn(ALOAD, count); + count++; + break; + } + mv.visitFieldInsn(ValhallaUtils.WITHFIELD, valueName, nameAndSig[0], nameAndSig[1]); + } + } + mv.visitInsn(ARETURN); + int maxStack = (doubleDetected) ? 3 : 2; + mv.visitMaxs(maxStack, makeMaxLocal); + mv.visitEnd(); + } + + private static void makeGeneric(ClassWriter cw, String className, String methodName, String specificMethodName, String makeValueSig, String makeValueGenericSig, String[] fields, int makeMaxLocal, boolean isRef) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, "(" + makeValueGenericSig + ")Ljava/lang/Object;", null, new String[] {"java/lang/Exception"}); + mv.visitCode(); + for (int i = 0; i < fields.length; i++) { + String nameAndSigValue[] = fields[i].split(":"); + if ((nameAndSigValue.length < 3) || !(nameAndSigValue[2].equals("static"))) { + mv.visitVarInsn(ALOAD, i); + switch (nameAndSigValue[1]) { + case "D": + mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + break; + case "I": + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + break; + case "Z": + mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + break; + case "B": + mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + break; + case "C": + mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); + break; + case "S": + mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + break; + case "F": + mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + break; + case "J": + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + break; + default: + String signature = nameAndSigValue[1]; + + if ('L' == signature.charAt(0)) { + signature = signature.substring(1, signature.length() - 1); + } + mv.visitTypeInsn(CHECKCAST, signature); + break; + } + } + } + mv.visitMethodInsn(INVOKESTATIC, className, specificMethodName, "(" + makeValueSig + ")" + "L" + className + ";", false); + mv.visitInsn(ARETURN); + int maxStack = makeMaxLocal; + if (0 == maxStack) { + maxStack += 1; + } + mv.visitMaxs(maxStack, makeMaxLocal); + 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.visitFrame(F_SAME, 3, new Object[] {INTEGER, containerClassName, "Ljava/lang/Object;"}, 0, new Object[]{}); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, containerClassName, nameAndSigValue[0], nameAndSigValue[1]); + mv.visitJumpInsn(GOTO, endLabel); + } + mv.visitLabel(defaultLabel); + mv.visitFrame(F_SAME, 3, new Object[] {INTEGER, containerClassName, "Ljava/lang/Object;"}, 0, new Object[]{}); + mv.visitInsn(ACONST_NULL); + mv.visitLabel(endLabel); + mv.visitFrame(F_SAME1, 3, new Object[] {INTEGER, containerClassName, "Ljava/lang/Object;"}, 1, new Object[]{"Ljava/lang/Object;"}); + 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.visitFrame(F_SAME, 3, new Object[] {INTEGER, containerClassName, "Ljava/lang/Object;"}, 0, new Object[]{}); + 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.visitFrame(F_SAME, 3, new Object[] {INTEGER, containerClassName, "Ljava/lang/Object;"}, 0, new Object[]{}); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + + private static void makeValueTypeDefaultValue(ClassWriter cw, String valueName, String makeValueSig, String[] fields, int makeMaxLocal, boolean isRef) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "makeValueTypeDefaultValue", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitTypeInsn(ValhallaUtils.ACONST_INIT, "L" + valueName + ";"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 0); + mv.visitEnd(); + } + + private static void generateGetterGeneric(ClassWriter cw, String[] nameAndSigValue, String className) { + boolean doubleDetected = false; + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "getGeneric" + nameAndSigValue[0], "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, className, "get" + nameAndSigValue[0], "()" + nameAndSigValue[1], false); + switch (nameAndSigValue[1]) { + case "D": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + doubleDetected = true; + break; + case "I": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + break; + case "Z": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + break; + case "B": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + break; + case "C": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + break; + case "S": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + break; + case "F": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + break; + case "J": + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + doubleDetected = true; + break; + default: + break; + } + + mv.visitInsn(ARETURN); + int maxStack = (doubleDetected ? 2 : 1); + mv.visitMaxs(maxStack, 1); + mv.visitEnd(); + } + + private static void generateGetter(ClassWriter cw, String[] nameAndSigValue, String className) { + boolean doubleDetected = false; + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "get" + nameAndSigValue[0], "()" + nameAndSigValue[1], null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, className, nameAndSigValue[0], nameAndSigValue[1]); + switch (nameAndSigValue[1]) { + case "D": + mv.visitInsn(DRETURN); + doubleDetected = true; + break; + case "I": + case "Z": + case "B": + case "C": + case "S": + mv.visitInsn(IRETURN); + break; + case "F": + mv.visitInsn(FRETURN); + break; + case "J": + mv.visitInsn(LRETURN); + doubleDetected = true; + break; + default: + mv.visitInsn(ARETURN); + break; + } + int maxStack = (doubleDetected ? 2 : 1); + mv.visitMaxs(maxStack, 1); + mv.visitEnd(); + } } diff --git a/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeTests.java b/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeTests.java index 0e3e7925ece..40a158aea08 100644 --- a/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeTests.java +++ b/test/functional/Valhalla/src_lw5/org/openj9/test/lworld/ValueTypeTests.java @@ -21,12 +21,15 @@ *******************************************************************************/ package org.openj9.test.lworld; +import static org.objectweb.asm.Opcodes.*; import static org.testng.Assert.*; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; +import java.lang.reflect.Array; +import java.util.ArrayList; import org.testng.annotations.Test; /* @@ -69,6 +72,10 @@ public class ValueTypeTests { static Object defaultObject = (Object)0xEEFFEEFF; static int defaultIntNew = 0x45456767; static long defaultLongNew = 0x11551155AAEEAAEEL; + static long defaultLongNew2 = 0x22662266BBFFBBFFL; + static long defaultLongNew3 = 0x33773377CC00CC00L; + static long defaultLongNew4 = 0x44884488DD11DD11L; + static long defaultLongNew5 = 0x55995599EE22EE22L; static double defaultDoubleNew = -123412341.21341234d; static float defaultFloatNew = -123423.12341234f; static Object defaultObjectNew = (Object)0xFFEEFFEE; @@ -95,6 +102,11 @@ void checkEqualPoint2D(int[] position) throws Throwable { assertEquals(this.x, position[0]); assertEquals(this.y, position[1]); } + + void checkFieldAccessWithDefaultValues() throws Throwable { + checkEqualPoint2D(defaultPointPositions1); + // TODO add putfield tests once withfield is replaced + } } static value class Point2DComplex { @@ -132,6 +144,11 @@ static value class FlattenedLine2D { this(new Point2D(positions[0]), new Point2D(positions[1])); } + void checkFieldAccessWithDefaultValues() throws Throwable { + checkEqualFlattenedLine2D(defaultLinePositions1); + // TODO add putfield tests once withfield is replaced + } + void checkEqualFlattenedLine2D(int[][] positions) throws Throwable { st.checkEqualPoint2D(positions[0]); en.checkEqualPoint2D(positions[1]); @@ -160,6 +177,12 @@ static value class Triangle2D { new Point2D(positions[2][1][0], positions[2][1][1]))); } + + void checkFieldAccessWithDefaultValues() throws Throwable { + checkEqualTriangle2D(defaultTrianglePositions); + // TODO add putfield tests once withfield is replaced + } + void checkEqualTriangle2D(int[][][] positions) throws Throwable { v1.checkEqualFlattenedLine2D(positions[0]); v2.checkEqualFlattenedLine2D(positions[1]); @@ -185,6 +208,10 @@ static value class ValueLong { ValueLong(long l) { this.l = l; } + + public long getL() { + return l; + } } static value class ValueDouble { @@ -267,6 +294,8 @@ static class AssortedRefWithLongAlignment { ValueInt! i; Triangle2D! tri; + AssortedRefWithLongAlignment() {} + AssortedRefWithLongAlignment(Point2D! point, FlattenedLine2D! line, ValueObject! o, ValueLong! l, ValueDouble! d, ValueInt! i, Triangle2D! tri) { this.point = point; this.line = line; @@ -501,113 +530,6 @@ void checkFieldsWithDefaults() throws Throwable { } } - static value class SingleBackfill { - long l; - Object o; - int i; - - public implicit SingleBackfill(); - - SingleBackfill(long l, Object o, int i) { - this.l = l; - this.o = o; - this.i = i; - } - } - - static value class ObjectBackfill { - long l; - Object o; - - public implicit ObjectBackfill(); - - ObjectBackfill(long l, Object o) { - this.l = l; - this.o = o; - } - } - - static value class FlatUnAlignedSingle { - ValueInt! i; - ValueInt! i2; - - public implicit FlatUnAlignedSingle(); - - FlatUnAlignedSingle(ValueInt! i, ValueInt! i2) { - this.i = i; - this.i2 = i2; - } - } - - static value class FlatUnAlignedSingleBackfill { - ValueLong! l; - FlatUnAlignedSingle! singles; - ValueObject! o; - - public implicit FlatUnAlignedSingleBackfill(); - - FlatUnAlignedSingleBackfill(ValueLong! l, FlatUnAlignedSingle! singles, ValueObject! o) { - this.l = l; - this.singles = singles; - this.o = o; - } - } - - static value class FlatUnAlignedSingleBackfill2 { - ValueLong! l; - FlatUnAlignedSingle! singles; - FlatUnAlignedSingle! singles2; - - public implicit FlatUnAlignedSingleBackfill2(); - - FlatUnAlignedSingleBackfill2(ValueLong! l, FlatUnAlignedSingle! singles, FlatUnAlignedSingle! singles2) { - this.l = l; - this.singles = singles; - this.singles2 = singles2; - - } - } - - static value class FlatUnAlignedObject { - ValueObject! o; - ValueObject! o2; - - public implicit FlatUnAlignedObject(); - - FlatUnAlignedObject(ValueObject! o, ValueObject! o2) { - this.o = o; - this.o2 = o2; - } - } - - static value class FlatUnAlignedObjectBackfill { - FlatUnAlignedObject! objects; - FlatUnAlignedObject! objects2; - ValueLong! l; - - public implicit FlatUnAlignedObjectBackfill(); - - FlatUnAlignedObjectBackfill(FlatUnAlignedObject! objects, FlatUnAlignedObject! objects2, ValueLong! l) { - this.objects = objects; - this.objects2 = objects2; - this.l = l; - } - } - - static value class FlatUnAlignedObjectBackfill2 { - ValueObject! o; - FlatUnAlignedObject! objects; - ValueLong! l; - - public implicit FlatUnAlignedObjectBackfill2(); - - FlatUnAlignedObjectBackfill2(ValueObject! o, FlatUnAlignedObject! objects, ValueLong! l) { - this.o = o; - this.objects = objects; - this.l = l; - } - } - static value class LargeValueObject { /* 16 flattened valuetype members */ ValueObject! v1; @@ -1197,155 +1119,917 @@ static public void testAssortedRefWithSingleAlignment() throws Throwable { a.checkFieldsWithDefaults(); } - //******************************************************************************* - // Value type tests for static fields - //******************************************************************************* - - static value class ClassWithOnlyStaticFieldsWithSingleAlignment { - static Triangle2D! tri; - static Point2D! point; - static FlattenedLine2D! line; - static ValueInt! i; - static ValueFloat! f; - static Triangle2D! tri2; - } + static value class SingleBackfill { + long l; + Object o; + int i; - @Test(priority=4) - static public void testStaticFieldsWithSingleAlignment() throws Throwable { - ClassWithOnlyStaticFieldsWithSingleAlignment c = new ClassWithOnlyStaticFieldsWithSingleAlignment(); - c.tri = new Triangle2D(defaultTrianglePositions); - c.point = new Point2D(defaultPointPositions1); - c.line = new FlattenedLine2D(defaultLinePositions1); - c.i = new ValueInt(defaultInt); - c.f = new ValueFloat(defaultFloat); - c.tri2 = new Triangle2D(defaultTrianglePositions); - - c.tri.checkEqualTriangle2D(defaultTrianglePositions); - c.point.checkEqualPoint2D(defaultPointPositions1); - c.line.checkEqualFlattenedLine2D(defaultLinePositions1); - assertEquals(c.i.i, defaultInt); - assertEquals(c.f.f, defaultFloat); - c.tri2.checkEqualTriangle2D(defaultTrianglePositions); - } + public implicit SingleBackfill(); - static value class ClassWithOnlyStaticFieldsWithLongAlignment { - static Point2D! point; - static FlattenedLine2D! line; - static ValueObject! o; - static ValueLong! l; - static ValueDouble! d; - static ValueInt! i; - static Triangle2D! tri; + SingleBackfill(long l, Object o, int i) { + this.l = l; + this.o = o; + this.i = i; + } } - @Test(priority=4) - static public void testStaticFieldsWithLongAlignment() throws Throwable { - ClassWithOnlyStaticFieldsWithLongAlignment c = new ClassWithOnlyStaticFieldsWithLongAlignment(); - c.point = new Point2D(defaultPointPositions1); - c.line = new FlattenedLine2D(defaultLinePositions1); - c.o = new ValueObject(defaultObject); - c.l = new ValueLong(defaultLong); - c.d = new ValueDouble(defaultDouble); - c.i = new ValueInt(defaultInt); - c.tri = new Triangle2D(defaultTrianglePositions); - - c.point.checkEqualPoint2D(defaultPointPositions1); - c.line.checkEqualFlattenedLine2D(defaultLinePositions1); - assertEquals(c.o.val, defaultObject); - assertEquals(c.l.l, defaultLong); - assertEquals(c.d.d, defaultDouble); - assertEquals(c.i.i, defaultInt); - c.tri.checkEqualTriangle2D(defaultTrianglePositions); - } + static value class ObjectBackfill { + long l; + Object o; - static value class ClassWithOnlyStaticFieldsWithObjectAlignment { - static Triangle2D! tri; - static Point2D! point; - static FlattenedLine2D! line; - static ValueObject! o; - static ValueInt! i; - static ValueFloat! f; - static Triangle2D! tri2; - } + public implicit ObjectBackfill(); - @Test(priority=4) - static public void testStaticFieldsWithObjectAlignment() throws Throwable { - ClassWithOnlyStaticFieldsWithObjectAlignment c = new ClassWithOnlyStaticFieldsWithObjectAlignment(); - c.tri = new Triangle2D(defaultTrianglePositions); - c.point = new Point2D(defaultPointPositions1); - c.line = new FlattenedLine2D(defaultLinePositions1); - c.o = new ValueObject(defaultObject); - c.i = new ValueInt(defaultInt); - c.f = new ValueFloat(defaultFloat); - c.tri2 = new Triangle2D(defaultTrianglePositions); - - c.tri.checkEqualTriangle2D(defaultTrianglePositions); - c.point.checkEqualPoint2D(defaultPointPositions1); - c.line.checkEqualFlattenedLine2D(defaultLinePositions1); - assertEquals(c.o.val, defaultObject); - assertEquals(c.i.i, defaultInt); - assertEquals(c.f.f, defaultFloat); - c.tri2.checkEqualTriangle2D(defaultTrianglePositions); + ObjectBackfill(long l, Object o) { + this.l = l; + this.o = o; + } } - //******************************************************************************* - // GC tests - //******************************************************************************* + static value class FlatSingleBackfill { + ValueLong! l; + ValueObject! o; + ValueInt! i; - @Test(priority=5) - static public void testGCFlattenedPoint2DArray() throws Throwable { - int x1 = 0xFFEEFFEE; - int y1 = 0xAABBAABB; - Point2D! point2D = new Point2D(x1, y1); - Point2D![] array = new Point2D![8]; + public implicit FlatSingleBackfill(); - for (int i = 0; i < 8; i++) { - array[i] = point2D; + FlatSingleBackfill(ValueLong! l, ValueObject! o, ValueInt! i) { + this.l = l; + this.o = o; + this.i = i; } + } - System.gc(); + static value class FlatObjectBackfill { + ValueLong! l; + ValueObject! o; - Point2D! value = array[0]; + public implicit FlatObjectBackfill(); + + FlatObjectBackfill(ValueLong! l, ValueObject! o) { + this.l = l; + this.o = o; + } } - @Test(priority=5) - static public void testGCFlattenedValueArrayWithSingleAlignment() throws Throwable { - int size = 4; - AssortedValueWithSingleAlignment![] array = new AssortedValueWithSingleAlignment![size]; + static value class FlatUnAlignedSingle { + ValueInt! i; + ValueInt! i2; - for (int i = 0; i < size; i++) { - array[i] = AssortedValueWithSingleAlignment.createObjectWithDefaults(); + public implicit FlatUnAlignedSingle(); + + FlatUnAlignedSingle(ValueInt! i, ValueInt! i2) { + this.i = i; + this.i2 = i2; } + } - System.gc(); + static value class FlatUnAlignedSingleBackfill { + ValueLong! l; + FlatUnAlignedSingle! singles; + ValueObject! o; - for (int i = 0; i < size; i++) { - array[i].checkFieldsWithDefaults(); + public implicit FlatUnAlignedSingleBackfill(); + + FlatUnAlignedSingleBackfill(ValueLong! l, FlatUnAlignedSingle! singles, ValueObject! o) { + this.l = l; + this.singles = singles; + this.o = o; } } - @Test(priority=5) - static public void testGCFlattenedValueArrayWithObjectAlignment() throws Throwable { - int size = 4; - AssortedValueWithObjectAlignment![] array = new AssortedValueWithObjectAlignment![size]; + static value class FlatUnAlignedSingleBackfill2 { + ValueLong! l; + FlatUnAlignedSingle! singles; + FlatUnAlignedSingle! singles2; - for (int i = 0; i < size; i++) { - array[i] = AssortedValueWithObjectAlignment.createObjectWithDefaults(); - } + public implicit FlatUnAlignedSingleBackfill2(); - System.gc(); + FlatUnAlignedSingleBackfill2(ValueLong! l, FlatUnAlignedSingle! singles, FlatUnAlignedSingle! singles2) { + this.l = l; + this.singles = singles; + this.singles2 = singles2; - for (int i = 0; i < size; i++) { - array[i].checkFieldsWithDefaults(); } } - @Test(priority=5) - static public void testGCFlattenedValueArrayWithLongAlignment() throws Throwable { - AssortedValueWithLongAlignment![] array = new AssortedValueWithLongAlignment![genericArraySize]; + static value class FlatUnAlignedObject { + ValueObject! o; + ValueObject! o2; - for (int i = 0; i < genericArraySize; i++) { - array[i] = AssortedValueWithLongAlignment.createObjectWithDefaults(); + public implicit FlatUnAlignedObject(); + + FlatUnAlignedObject(ValueObject! o, ValueObject! o2) { + this.o = o; + this.o2 = o2; + } + } + + static value class FlatUnAlignedObjectBackfill { + FlatUnAlignedObject! objects; + FlatUnAlignedObject! objects2; + ValueLong! l; + + public implicit FlatUnAlignedObjectBackfill(); + + FlatUnAlignedObjectBackfill(FlatUnAlignedObject! objects, FlatUnAlignedObject! objects2, ValueLong! l) { + this.objects = objects; + this.objects2 = objects2; + this.l = l; + } + } + + static value class FlatUnAlignedObjectBackfill2 { + ValueObject! o; + FlatUnAlignedObject! objects; + ValueLong! l; + + public implicit FlatUnAlignedObjectBackfill2(); + + FlatUnAlignedObjectBackfill2(ValueObject! o, FlatUnAlignedObject! objects, ValueLong! l) { + this.o = o; + this.objects = objects; + this.l = l; + } + } + + @Test(priority=3, invocationCount=2) + static public void testLayoutsWithNullRestrictedFields() throws Throwable { + SingleBackfill! singleBackfillInstance = new SingleBackfill(defaultLong, defaultObject, defaultInt); + assertEquals(singleBackfillInstance.i, defaultInt); + assertEquals(singleBackfillInstance.o, defaultObject); + assertEquals(singleBackfillInstance.l, defaultLong); + + ObjectBackfill! objectBackfillInstance = new ObjectBackfill(defaultLong, defaultObject); + assertEquals(objectBackfillInstance.o, defaultObject); + assertEquals(objectBackfillInstance.l, defaultLong); + } + + @Test(priority=5, invocationCount=2) + static public void testFlatLayoutsWithValueTypes() throws Throwable { + FlatSingleBackfill! flatSingleBackfillInstance = new FlatSingleBackfill(new ValueLong(defaultLong), new ValueObject(defaultObject), new ValueInt(defaultInt)); + assertEquals(flatSingleBackfillInstance.i.i, defaultInt); + assertEquals(flatSingleBackfillInstance.o.val, defaultObject); + assertEquals(flatSingleBackfillInstance.l.l, defaultLong); + + FlatObjectBackfill! objectBackfillInstance = new FlatObjectBackfill(new ValueLong(defaultLong), new ValueObject(defaultObject)); + assertEquals(objectBackfillInstance.o.val, defaultObject); + assertEquals(objectBackfillInstance.l.l, defaultLong); + + FlatUnAlignedSingleBackfill! flatUnAlignedSingleBackfillInstance = + new FlatUnAlignedSingleBackfill(new ValueLong(defaultLong), new FlatUnAlignedSingle(new ValueInt(defaultInt), new ValueInt(defaultIntNew)), new ValueObject(defaultObject)); + assertEquals(flatUnAlignedSingleBackfillInstance.l.l, defaultLong); + assertEquals(flatUnAlignedSingleBackfillInstance.o.val, defaultObject); + assertEquals(flatUnAlignedSingleBackfillInstance.singles.i.i, defaultInt); + assertEquals(flatUnAlignedSingleBackfillInstance.singles.i2.i, defaultIntNew); + + FlatUnAlignedSingleBackfill2! flatUnAlignedSingleBackfill2Instance = + new FlatUnAlignedSingleBackfill2(new ValueLong(defaultLong), new FlatUnAlignedSingle(new ValueInt(defaultInt), new ValueInt(defaultIntNew)), new FlatUnAlignedSingle(new ValueInt(defaultInt), new ValueInt(defaultIntNew))); + assertEquals(flatUnAlignedSingleBackfill2Instance.l.l, defaultLong); + assertEquals(flatUnAlignedSingleBackfill2Instance.singles.i.i, defaultInt); + assertEquals(flatUnAlignedSingleBackfill2Instance.singles.i2.i, defaultIntNew); + assertEquals(flatUnAlignedSingleBackfill2Instance.singles2.i.i, defaultInt); + assertEquals(flatUnAlignedSingleBackfill2Instance.singles2.i2.i, defaultIntNew); + + FlatUnAlignedObjectBackfill! flatUnAlignedObjectBackfillInstance = + new FlatUnAlignedObjectBackfill(new FlatUnAlignedObject(new ValueObject(defaultObject), new ValueObject(defaultObjectNew)), + new FlatUnAlignedObject(new ValueObject(defaultObject), new ValueObject(defaultObjectNew)), new ValueLong(defaultLong)); + assertEquals(flatUnAlignedObjectBackfillInstance.l.l, defaultLong); + assertEquals(flatUnAlignedObjectBackfillInstance.objects.o.val, defaultObject); + assertEquals(flatUnAlignedObjectBackfillInstance.objects.o2.val, defaultObjectNew); + assertEquals(flatUnAlignedObjectBackfillInstance.objects2.o.val, defaultObject); + assertEquals(flatUnAlignedObjectBackfillInstance.objects2.o2.val, defaultObjectNew); + + FlatUnAlignedObjectBackfill2! flatUnAlignedObjectBackfill2Instance = + new FlatUnAlignedObjectBackfill2(new ValueObject(defaultObject), new FlatUnAlignedObject(new ValueObject(defaultObject), new ValueObject(defaultObjectNew)), new ValueLong(defaultLong)); + assertEquals(flatUnAlignedObjectBackfill2Instance.l.l, defaultLong); + assertEquals(flatUnAlignedObjectBackfill2Instance.objects.o.val, defaultObject); + assertEquals(flatUnAlignedObjectBackfill2Instance.objects.o2.val, defaultObjectNew); + assertEquals(flatUnAlignedObjectBackfill2Instance.o.val, defaultObject); + } + + static value class ValueTypeDoubleLong { + final ValueLong! l; + final long l2; + public implicit ValueTypeDoubleLong(); + ValueTypeDoubleLong(ValueLong l, long l2) { + this.l = l; + this.l2 = l2; + } + public ValueLong getL() { + return l; + } + public long getL2() { + return l2; + } + } + + static value class ValueTypeQuadLong { + final ValueTypeDoubleLong! l; + final ValueLong! l2; + final long l3; + public implicit ValueTypeQuadLong(); + ValueTypeQuadLong(ValueTypeDoubleLong l, ValueLong l2, long l3) { + this.l = l; + this.l2 = l2; + this.l3 = l3; + } + public ValueTypeDoubleLong getL() { + return l; + } + public ValueLong getL2() { + return l2; + } + public long getL3() { + return l3; + } + } + + static value class ValueTypeDoubleQuadLong { + final ValueTypeQuadLong! l; + final ValueTypeDoubleLong! l2; + final ValueLong! l3; + final long l4; + public implicit ValueTypeDoubleQuadLong(); + ValueTypeDoubleQuadLong(ValueTypeQuadLong l, ValueTypeDoubleLong l2, ValueLong l3, long l4) { + this.l = l; + this.l2 = l2; + this.l3 = l3; + this.l4 = l4; + } + public ValueTypeQuadLong getL() { + return l; + } + public ValueTypeDoubleLong getL2() { + return l2; + } + public ValueLong getL3() { + return l3; + } + public long getL4() { + return l4; + } + } + + @Test(priority=4, invocationCount=2) + static public void testFlatLayoutsWithRecursiveLongs() throws Throwable { + ValueTypeDoubleLong! doubleLongInstance = new ValueTypeDoubleLong(new ValueLong(defaultLong), defaultLongNew); + assertEquals(doubleLongInstance.getL().getL(), defaultLong); + assertEquals(doubleLongInstance.getL2(), defaultLongNew); + + ValueTypeQuadLong! quadLongInstance = new ValueTypeQuadLong(doubleLongInstance, new ValueLong(defaultLongNew2), defaultLongNew3); + assertEquals(quadLongInstance.getL().getL().getL(), defaultLong); + assertEquals(quadLongInstance.getL().getL2(), defaultLongNew); + assertEquals(quadLongInstance.getL2().getL(), defaultLongNew2); + assertEquals(quadLongInstance.getL3(), defaultLongNew3); + + ValueTypeDoubleQuadLong! doubleQuadLongInstance = new ValueTypeDoubleQuadLong(quadLongInstance, doubleLongInstance, new ValueLong(defaultLongNew4), defaultLongNew5); + + assertEquals(doubleQuadLongInstance.getL().getL().getL().getL(), defaultLong); + assertEquals(doubleQuadLongInstance.getL().getL().getL2(), defaultLongNew); + assertEquals(doubleQuadLongInstance.getL().getL2().getL(), defaultLongNew2); + assertEquals(doubleQuadLongInstance.getL().getL3(), defaultLongNew3); + assertEquals(doubleQuadLongInstance.getL2().getL().getL(), defaultLong); + assertEquals(doubleQuadLongInstance.getL2().getL2(), defaultLongNew); + assertEquals(doubleQuadLongInstance.getL3().getL(), defaultLongNew4); + assertEquals(doubleQuadLongInstance.getL4(), defaultLongNew5); + } + + static value class NestedA { + int x; + int y; + + public implicit NestedA(); + } + + static value class NestedB { + NestedA! a; + NestedA! b; + + public implicit NestedB(); + } + + static value class ContainerC { + NestedB! c; + NestedB! d; + + public implicit ContainerC(); + } + + @Test(priority=1) + static public void testFlattenedFieldInitSequence() throws Throwable { + ContainerC! c = new ContainerC(); + assertNotNull(c.c); + assertNotNull(c.c.a); + assertNotNull(c.c.b); + + assertNotNull(c.d); + assertNotNull(c.d.a); + assertNotNull(c.d.b); + } + + private static class UnresolvedClassDesc { + public String name; + public String[] fields; + public UnresolvedClassDesc(String name, String[] fields) { + this.name = name; + this.fields = fields; + } + } + + /* + * Test use of GETFIELD operations on the fields of a container class, where + * the fields are of value type classes that have not been resolved. + * + * The method is first called so that the GETFIELD will not be executed, and + * the class not resolved, and then called so that the GETFIELD and class + * resolution is triggered. + */ + @Test(priority=1) + static public void testUnresolvedGetFieldUse() throws Throwable { + /* + * Set up classes that look roughly like this: + * + * public inline class UnresolvedB1 { + * public final int a; + * public final int b; + * } + * + * public inline class UnresolvedB2 { + * public final int c; + * public final int d; + * } + * + * public inline class UnresolvedB3 { + * public final int e; + * public final int f; + * } + * + * public class ContainerForUnresolvedB { + * public UnresolvedB1! v1; + * public UnresolvedB2! v2; + * public UnresolvedB3! v3; + * } + * + * public class UsingUnresolvedB { + * public Object testUnresolvedValueTypeGetField(int fieldNum, ContainerForUnresolvedB container) { + * // Passing in a value in the range [0..2] triggers execution of a GETFIELD + * // operation on the corresponding field of "container", triggering + * // resolution of the field. Passing in a value outside that range, delays + * // triggering resolution of the field + * // + * switch (fieldNum) { + * case 0: return container.v1; + * case 1: return container.v2; + * case 2: return container.v3; + * default: return null; + * } + * } + * } + */ + UnresolvedClassDesc[] uclassDescArr = new UnresolvedClassDesc[] { + new UnresolvedClassDesc("UnresolvedB1", new String[] {"a:I", "b:I"}), + new UnresolvedClassDesc("UnresolvedB2", new String[] {"c:I", "d:I"}), + new UnresolvedClassDesc("UnresolvedB3", new String[] {"e:I", "f:I"})}; + + Class[] valueClassArr = new Class[uclassDescArr.length]; + String containerFields[] = new String[uclassDescArr.length]; + MethodHandle[][] valueFieldGetters = new MethodHandle[uclassDescArr.length][]; + + for (int i = 0; i < uclassDescArr.length; i++) { + UnresolvedClassDesc desc = uclassDescArr[i]; + valueClassArr[i] = ValueTypeGenerator.generateValueClass(desc.name, desc.fields); + valueFieldGetters[i] = new MethodHandle[desc.fields.length]; + containerFields[i] = "v"+(i+1)+":L"+desc.name+";::NR"; + + for (int j = 0; j < desc.fields.length; j++) { + String[] nameAndSig = desc.fields[j].split(":"); + valueFieldGetters[i][j] = generateGenericGetter(valueClassArr[i], nameAndSig[0]); + } + } + + Class containerClass = ValueTypeGenerator.generateRefClass("ContainerForUnresolvedB", containerFields); + + String fieldsUsing[] = {}; + Class usingClass = ValueTypeGenerator.generateRefClass("UsingUnresolvedB", fieldsUsing, "ContainerForUnresolvedB", containerFields); + + MethodHandle getFieldUnresolved = lookup.findStatic(usingClass, "testUnresolvedValueTypeGetField", + MethodType.methodType(Object.class, new Class[] {int.class, containerClass})); + + for (int i = 0; i < 10; i++) { + /* + * Pass -1 to avoid execution of GETFIELD against field that has a value type class + * In turn that delays the resolution of the value type class + */ + assertNull(getFieldUnresolved.invoke(-1, null)); + } + + Object containerObject = containerClass.newInstance(); + for (int i = 0; i < uclassDescArr.length; i++) { + /* + * Pass 0 or more to trigger execution of GETFIELD against field that has a value type class + * In turn that triggers the resolution of the associated value type classes + */ + Object fieldVal = getFieldUnresolved.invoke(i, containerObject); + assertNotNull(fieldVal); + + for (int j = 0; j < valueFieldGetters[i].length; j++) { + assertEquals(valueFieldGetters[i][j].invoke(fieldVal), Integer.valueOf(0)); + } + } + } + + /* + * Test use of PUTFIELD operations on the fields of a container class, where + * the fields are of value type classes that have not been resolved. + * + * The method is first called so that the PUTFIELD will not be executed, and + * the class not resolved, and then called so that the PUTFIELD and class + * resolution is triggered. + */ + @Test(priority=1) + static public void testUnresolvedPutFieldUse() throws Throwable { + /* + * Set up classes that look roughly like this: + * + * public inline class UnresolvedC1 { + * public final int a; + * public final int b; + * } + * + * public inline class UnresolvedC2 { + * public final int c; + * public final int d; + * } + * + * public inline class UnresolvedC3 { + * public final int e; + * public final int f; + * } + * + * public class ContainerForUnresolvedC { + * public UnresolvedC1 v1; + * public UnresolvedC2 v2; + * public UnresolvedC3 v3; + * } + * + * public class UsingUnresolvedC { + * public Object testUnresolvedValueTypePutField(int fieldNum, ContainerForUnresolvedC container, Object val) { + * // Passing in a value in the range [0..2] triggers execution of a PUTFIELD + * // operation on the corresponding field of "container", triggering + * // resolution of the class and field. Passing in a value outside that range, + * // delays triggering that resolution + * // + * switch (fieldNum) { + * case 0: container.v1 = (UnresolvedC3) val; break; + * case 1: container.v2 = (UnresolvedC2) val; break; + * case 2: container.v3 = (UnresolvedC3) val; break; + * default: break; + * } + * } + * } + */ + UnresolvedClassDesc[] uclassDescArr = new UnresolvedClassDesc[] { + new UnresolvedClassDesc("UnresolvedC1", new String[] {"a:I", "b:I"}), + new UnresolvedClassDesc("UnresolvedC2", new String[] {"c:I", "d:I"}), + new UnresolvedClassDesc("UnresolvedC3", new String[] {"e:I", "f:I"})}; + + Class[] valueClassArr = new Class[uclassDescArr.length]; + String containerFields[] = new String[uclassDescArr.length]; + MethodHandle[][] valueFieldGetters = new MethodHandle[uclassDescArr.length][]; + MethodHandle[] containerFieldGetters = new MethodHandle[uclassDescArr.length]; + + for (int i = 0; i < uclassDescArr.length; i++) { + UnresolvedClassDesc desc = uclassDescArr[i]; + valueClassArr[i] = ValueTypeGenerator.generateValueClass(desc.name, desc.fields); + valueFieldGetters[i] = new MethodHandle[desc.fields.length]; + containerFields[i] = "v"+(i+1)+":L"+desc.name+";::NR"; + + for (int j = 0; j < desc.fields.length; j++) { + String[] nameAndSig = desc.fields[j].split(":"); + valueFieldGetters[i][j] = generateGenericGetter(valueClassArr[i], nameAndSig[0]); + } + } + + Class containerClass = ValueTypeGenerator.generateRefClass("ContainerForUnresolvedC", containerFields); + + for (int i = 0; i < uclassDescArr.length; i++) { + String[] nameAndSig = containerFields[i].split(":"); + containerFieldGetters[i] = generateGenericGetter(containerClass, nameAndSig[0]); + } + + String fieldsUsing[] = {}; + Class usingClass = ValueTypeGenerator.generateRefClass("UsingUnresolvedC", fieldsUsing, "ContainerForUnresolvedC", containerFields); + + MethodHandle putFieldUnresolved = lookup.findStatic(usingClass, "testUnresolvedValueTypePutField", + MethodType.methodType(void.class, new Class[] {int.class, containerClass, Object.class})); + + for (int i = 0; i < 10; i++) { + /* + * Pass -1 to avoid execution of PUTFIELD into field that has a value type class + * In turn that delays the resolution of the value type class + */ + putFieldUnresolved.invoke(-1, null, null); + } + + Object containerObject = containerClass.newInstance(); + for (int i = 0; i < uclassDescArr.length; i++) { + MethodHandle makeDefaultValue = lookup.findStatic(valueClassArr[i], "makeValueTypeDefaultValue", MethodType.methodType(Object.class)); + Object valueObject = makeDefaultValue.invoke(); + /* + * Pass 0 or more to trigger execution of PUTFIELD against field that has a value type class + * In turn that triggers the resolution of the associated value type classes + */ + putFieldUnresolved.invoke(i, containerObject, valueObject); + } + + for (int i = 0; i < containerFieldGetters.length; i++) { + Object containerFieldValue = containerFieldGetters[i].invoke(containerObject); + + for (int j = 0; j < valueFieldGetters[i].length; j++) { + assertEquals(valueFieldGetters[i][j].invoke(containerFieldValue), Integer.valueOf(0)); + } + } + } + + static MethodHandle generateGenericGetter(Class clazz, String fieldName) { + try { + return lookup.findVirtual(clazz, "getGeneric"+fieldName, MethodType.methodType(Object.class)); + } catch (IllegalAccessException | SecurityException | NullPointerException | NoSuchMethodException e) { + e.printStackTrace(); + } + return null; + } + + @Test(priority = 1) + static public void testIdentityObjectOnValueType() throws Throwable { + assertTrue(ValueLong.class.isValue()); + assertFalse(ValueLong.class.isIdentity()); + } + + @Test(priority=1) + static public void testIdentityObjectOnHeavyAbstract() throws Throwable { + assertFalse(HeavyAbstractClass.class.isValue()); + assertTrue(HeavyAbstractClass.class.isIdentity()); + } + + public static abstract class HeavyAbstractClass { + /* Abstract class that has fields */ + private int x; + private int y; + public int getX(){ + return x; + } + public int getY(){ + return y; + } + } + + public static abstract class HeavyAbstractClass1 { + /* Abstract class that has non-empty constructors */ + public HeavyAbstractClass1() { + System.out.println("This class is HeavyAbstractClass1"); + } + } + + public static abstract class HeavyAbstractClass2 { + /* Abstract class that has synchronized methods */ + public synchronized void printClassName() { + System.out.println("This class is HeavyAbstractClass2"); + } + } + + public static abstract class HeavyAbstractClass3 { + /* Abstract class that has initializer */ + { System.out.println("This class is HeavyAbstractClass3"); } + } + + public static abstract class LightAbstractClass { + + } + + private static abstract class InvisibleLightAbstractClass { + + } + + static private void valueTypeIdentityObjectTestHelper(String testName, String superClassName, int extraClassFlags) throws Throwable { + String fields[] = {"longField:J"}; + if (null == superClassName) { + superClassName = "java/lang/Object"; + } + Class valueClass = ValueTypeGenerator.generateValueClass(testName, superClassName, fields, extraClassFlags); + assertTrue(valueClass.isValue()); + } + + @Test(priority=1, expectedExceptions=IncompatibleClassChangeError.class) + static public void testValueTypeSubClassHeavyAbstract() throws Throwable { + String superClassName = HeavyAbstractClass.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassHeavyAbstract", superClassName, 0); + } + + @Test(priority=1, expectedExceptions=IncompatibleClassChangeError.class) + static public void testValueTypeSubClassHeavyAbstract1() throws Throwable { + String superClassName = HeavyAbstractClass1.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassHeavyAbstract1", superClassName, 0); + } + + @Test(priority=1, expectedExceptions=IncompatibleClassChangeError.class) + static public void testValueTypeSubClassHeavyAbstract2() throws Throwable { + String superClassName = HeavyAbstractClass2.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassHeavyAbstract2", superClassName, 0); + } + + @Test(priority=1, expectedExceptions=IncompatibleClassChangeError.class) + static public void testValueTypeSubClassHeavyAbstract3() throws Throwable { + String superClassName = HeavyAbstractClass3.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassHeavyAbstract3", superClassName, 0); + } + + @Test(priority=1) + static public void testValueTypeSubClassLightAbstract() throws Throwable { + String superClassName = LightAbstractClass.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassLightAbstract", superClassName, 0); + } + + @Test(priority=1, expectedExceptions=ClassFormatError.class) + /* RI throws ClassFormatError for this case. */ + static public void testInterfaceValueType() throws Throwable { + valueTypeIdentityObjectTestHelper("testInterfaceValueType", "java/lang/Object", ACC_INTERFACE | ACC_ABSTRACT); + } + + @Test(priority=1, expectedExceptions=ClassFormatError.class) + /* RI throws ClassFormatError for this case. */ + static public void testAbstractValueType() throws Throwable { + valueTypeIdentityObjectTestHelper("testAbstractValueType", "java/lang/Object", ACC_ABSTRACT); + } + + @Test(priority=1, expectedExceptions=IllegalAccessError.class) + static public void testValueTypeSubClassInvisibleLightAbstractClass() throws Throwable { + String superClassName = InvisibleLightAbstractClass.class.getName().replace('.', '/'); + valueTypeIdentityObjectTestHelper("testValueTypeSubClassInvisibleLightAbstractClass", superClassName, 0); + } + + @Test(priority = 1) + static public void testIdentityObjectOnJLObject() throws Throwable { + assertFalse(Object.class.isValue()); + assertFalse(Object.class.isIdentity()); + } + + static class IdentityObject { + long longField; + } + + @Test(priority = 1) + static public void testIdentityObjectOnRef() throws Throwable { + assertFalse(IdentityObject.class.isValue()); + assertTrue(IdentityObject.class.isIdentity()); + } + + @Test(priority=1) + static public void testIsValueClassOnValueType() throws Throwable { + assertTrue(ValueLong.class.isValue()); + assertFalse(ValueLong.class.isIdentity()); + } + + private interface TestInterface {} + + @Test(priority=1) + static public void testIsValueClassOnInterface() throws Throwable { + assertFalse(TestInterface.class.isValue()); + assertFalse(TestInterface.class.isIdentity()); + } + + @Test(priority=1) + static public void testIsValueClassOnAbstractClass() throws Throwable { + assertFalse(HeavyAbstractClass.class.isValue()); + assertTrue(HeavyAbstractClass.class.isIdentity()); + } + + @Test(priority=1) + static public void testIsValueOnValueArrayClass() throws Throwable { + assertFalse(ValueLong.class.arrayType().isValue()); + assertTrue(ValueLong.class.arrayType().isIdentity()); + } + + static class TestIsValueOnRefArrayClass { + long longField; + } + + @Test(priority=1) + static public void testIsValueOnRefArrayClass() throws Throwable { + assertFalse(TestIsValueOnRefArrayClass.class.arrayType().isValue()); + assertTrue(TestIsValueOnRefArrayClass.class.arrayType().isIdentity()); + } + + static value class ValueIntPoint2D { + final ValueInt x, y; + + ValueIntPoint2D(ValueInt x, ValueInt y) { + this.x = x; + this.y = y; + } + } + + @Test(priority=1) + static public void testValueClassHashCode() throws Throwable { + Object p1 = new ValueIntPoint2D(new ValueInt(1), new ValueInt(2)); + Object p2 = new ValueIntPoint2D(new ValueInt(1), new ValueInt(2)); + Object p3 = new ValueIntPoint2D(new ValueInt(2), new ValueInt(2)); + Object p4 = new ValueIntPoint2D(new ValueInt(2), new ValueInt(1)); + Object p5 = new ValueIntPoint2D(new ValueInt(3), new ValueInt(4)); + + int h1 = p1.hashCode(); + int h2 = p2.hashCode(); + int h3 = p3.hashCode(); + int h4 = p4.hashCode(); + int h5 = p5.hashCode(); + + assertEquals(h1, h2); + assertNotEquals(h1, h3); + assertNotEquals(h1, h4); + assertNotEquals(h1, h5); + } + + //******************************************************************************* + // Value type tests for static fields + //******************************************************************************* + + static value class ClassWithOnlyStaticFieldsWithSingleAlignment { + static Triangle2D! tri; + static Point2D! point; + static FlattenedLine2D! line; + static ValueInt! i; + static ValueFloat! f; + static Triangle2D! tri2; + } + + /* Prioritize before static fields are set by testStaticFieldsWithSingleAlignment */ + @Test(priority=3, invocationCount=2) + static public void testStaticFieldsWithSingleAlignmenDefaultValues() throws Throwable { + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.tri); + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.point); + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.line); + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.i); + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.f); + assertNotNull(ClassWithOnlyStaticFieldsWithSingleAlignment.tri2); + } + + @Test(priority=4) + static public void testStaticFieldsWithSingleAlignment() throws Throwable { + ClassWithOnlyStaticFieldsWithSingleAlignment.tri = new Triangle2D(defaultTrianglePositions); + ClassWithOnlyStaticFieldsWithSingleAlignment.point = new Point2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithSingleAlignment.line = new FlattenedLine2D(defaultLinePositions1); + ClassWithOnlyStaticFieldsWithSingleAlignment.i = new ValueInt(defaultInt); + ClassWithOnlyStaticFieldsWithSingleAlignment.f = new ValueFloat(defaultFloat); + ClassWithOnlyStaticFieldsWithSingleAlignment.tri2 = new Triangle2D(defaultTrianglePositions); + + ClassWithOnlyStaticFieldsWithSingleAlignment.tri.checkEqualTriangle2D(defaultTrianglePositions); + ClassWithOnlyStaticFieldsWithSingleAlignment.point.checkEqualPoint2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithSingleAlignment.line.checkEqualFlattenedLine2D(defaultLinePositions1); + assertEquals(ClassWithOnlyStaticFieldsWithSingleAlignment.i.i, defaultInt); + assertEquals(ClassWithOnlyStaticFieldsWithSingleAlignment.f.f, defaultFloat); + ClassWithOnlyStaticFieldsWithSingleAlignment.tri2.checkEqualTriangle2D(defaultTrianglePositions); + } + + static value class ClassWithOnlyStaticFieldsWithLongAlignment { + static Point2D! point; + static FlattenedLine2D! line; + static ValueObject! o; + static ValueLong! l; + static ValueDouble! d; + static ValueInt! i; + static Triangle2D! tri; + } + + /* Prioritize before static fields are set by testStaticFieldsWithLongAlignment */ + @Test(priority=3, invocationCount=2) + static public void testStaticFieldsWithLongAlignmenDefaultValues() throws Throwable { + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.point); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.line); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.o); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.l); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.d); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.i); + assertNotNull(ClassWithOnlyStaticFieldsWithLongAlignment.tri); + } + + @Test(priority=4) + static public void testStaticFieldsWithLongAlignment() throws Throwable { + ClassWithOnlyStaticFieldsWithLongAlignment.point = new Point2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithLongAlignment.line = new FlattenedLine2D(defaultLinePositions1); + ClassWithOnlyStaticFieldsWithLongAlignment.o = new ValueObject(defaultObject); + ClassWithOnlyStaticFieldsWithLongAlignment.l = new ValueLong(defaultLong); + ClassWithOnlyStaticFieldsWithLongAlignment.d = new ValueDouble(defaultDouble); + ClassWithOnlyStaticFieldsWithLongAlignment.i = new ValueInt(defaultInt); + ClassWithOnlyStaticFieldsWithLongAlignment.tri = new Triangle2D(defaultTrianglePositions); + + ClassWithOnlyStaticFieldsWithLongAlignment.point.checkEqualPoint2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithLongAlignment.line.checkEqualFlattenedLine2D(defaultLinePositions1); + assertEquals(ClassWithOnlyStaticFieldsWithLongAlignment.o.val, defaultObject); + assertEquals(ClassWithOnlyStaticFieldsWithLongAlignment.l.l, defaultLong); + assertEquals(ClassWithOnlyStaticFieldsWithLongAlignment.d.d, defaultDouble); + assertEquals(ClassWithOnlyStaticFieldsWithLongAlignment.i.i, defaultInt); + ClassWithOnlyStaticFieldsWithLongAlignment.tri.checkEqualTriangle2D(defaultTrianglePositions); + } + + static value class ClassWithOnlyStaticFieldsWithObjectAlignment { + static Triangle2D! tri; + static Point2D! point; + static FlattenedLine2D! line; + static ValueObject! o; + static ValueInt! i; + static ValueFloat! f; + static Triangle2D! tri2; + } + + /* Prioritize before static fields are set by testStaticFieldsWithObjectAlignment */ + @Test(priority=3, invocationCount=2) + static public void testStaticFieldsWithObjectAlignmentDefaultValues() throws Throwable { + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.tri); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.point); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.line); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.o); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.i); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.f); + assertNotNull(ClassWithOnlyStaticFieldsWithObjectAlignment.tri2); + } + + @Test(priority=4) + static public void testStaticFieldsWithObjectAlignment() throws Throwable { + ClassWithOnlyStaticFieldsWithObjectAlignment.tri = new Triangle2D(defaultTrianglePositions); + ClassWithOnlyStaticFieldsWithObjectAlignment.point = new Point2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithObjectAlignment.line = new FlattenedLine2D(defaultLinePositions1); + ClassWithOnlyStaticFieldsWithObjectAlignment.o = new ValueObject(defaultObject); + ClassWithOnlyStaticFieldsWithObjectAlignment.i = new ValueInt(defaultInt); + ClassWithOnlyStaticFieldsWithObjectAlignment.f = new ValueFloat(defaultFloat); + ClassWithOnlyStaticFieldsWithObjectAlignment.tri2 = new Triangle2D(defaultTrianglePositions); + + ClassWithOnlyStaticFieldsWithObjectAlignment.tri.checkEqualTriangle2D(defaultTrianglePositions); + ClassWithOnlyStaticFieldsWithObjectAlignment.point.checkEqualPoint2D(defaultPointPositions1); + ClassWithOnlyStaticFieldsWithObjectAlignment.line.checkEqualFlattenedLine2D(defaultLinePositions1); + assertEquals(ClassWithOnlyStaticFieldsWithObjectAlignment.o.val, defaultObject); + assertEquals(ClassWithOnlyStaticFieldsWithObjectAlignment.i.i, defaultInt); + assertEquals(ClassWithOnlyStaticFieldsWithObjectAlignment.f.f, defaultFloat); + ClassWithOnlyStaticFieldsWithObjectAlignment.tri2.checkEqualTriangle2D(defaultTrianglePositions); + } + + //******************************************************************************* + // GC tests + //******************************************************************************* + + @Test(priority=5) + static public void testGCFlattenedPoint2DArray() throws Throwable { + int x1 = 0xFFEEFFEE; + int y1 = 0xAABBAABB; + Point2D! point2D = new Point2D(x1, y1); + Point2D![] array = new Point2D![8]; + + for (int i = 0; i < 8; i++) { + array[i] = point2D; + } + + System.gc(); + + Point2D! value = array[0]; + } + + @Test(priority=5) + static public void testGCFlattenedValueArrayWithSingleAlignment() throws Throwable { + int size = 4; + AssortedValueWithSingleAlignment![] array = new AssortedValueWithSingleAlignment![size]; + + for (int i = 0; i < size; i++) { + array[i] = AssortedValueWithSingleAlignment.createObjectWithDefaults(); + } + + System.gc(); + + for (int i = 0; i < size; i++) { + array[i].checkFieldsWithDefaults(); + } + } + + @Test(priority=5) + static public void testGCFlattenedValueArrayWithObjectAlignment() throws Throwable { + int size = 4; + AssortedValueWithObjectAlignment![] array = new AssortedValueWithObjectAlignment![size]; + + for (int i = 0; i < size; i++) { + array[i] = AssortedValueWithObjectAlignment.createObjectWithDefaults(); + } + + System.gc(); + + for (int i = 0; i < size; i++) { + array[i].checkFieldsWithDefaults(); + } + } + + @Test(priority=5) + static public void testGCFlattenedValueArrayWithLongAlignment() throws Throwable { + AssortedValueWithLongAlignment![] array = new AssortedValueWithLongAlignment![genericArraySize]; + + for (int i = 0; i < genericArraySize; i++) { + array[i] = AssortedValueWithLongAlignment.createObjectWithDefaults(); } System.gc(); @@ -1386,6 +2070,100 @@ static public void testGCFlattenedMegaObjectArray() throws Throwable { MegaValueObject! value = array[0]; } + /* + * Create large number of value types and instantiate them + * + * value Point2D { + * int x; + * int y; + * } + */ + @Test(priority=1) + static public void testCreateLargeNumberOfPoint2D() throws Throwable { + String fields[] = {"x:I", "y:I"}; + for (int valueIndex = 0; valueIndex < objectGCScanningIterationCount; valueIndex++) { + String className = "Point2D" + valueIndex; + Class point2DXClass = ValueTypeGenerator.generateValueClass(className, fields); + /* findStatic will trigger class resolution */ + MethodHandle makePoint2DX = lookup.findStatic(point2DXClass, "staticMethod", MethodType.methodType(void.class)); + if (0 == (valueIndex % 100)) { + System.gc(); + } + } + } + + /* + * Maintain a buffer of flattened arrays with long-aligned valuetypes while keeping a certain amount of classes alive at any + * single time. This forces the GC to unload the classes. + */ + @Test(priority=5, invocationCount=2) + static public void testValueWithLongAlignmentGCScanning() throws Throwable { + ArrayList longAlignmentArrayList = new ArrayList(objectGCScanningIterationCount); + for (int i = 0; i < objectGCScanningIterationCount; i++) { + AssortedValueWithLongAlignment![] array = new AssortedValueWithLongAlignment![genericArraySize]; + for (int j = 0; j < genericArraySize; j++) { + array[j] = AssortedValueWithLongAlignment.createObjectWithDefaults(); + } + longAlignmentArrayList.add(array); + } + + System.gc(); + + for (int i = 0; i < objectGCScanningIterationCount; i++) { + for (int j = 0; j < genericArraySize; j++) { + longAlignmentArrayList.get(i)[j].checkFieldsWithDefaults(); + } + } + } + + /* + * Maintain a buffer of flattened arrays with object-aligned valuetypes while keeping a certain amount of classes alive at any + * single time. This forces the GC to unload the classes. + */ + @Test(priority=5, invocationCount=2) + static public void testValueWithObjectAlignmentGCScanning() throws Throwable { + ArrayList objectAlignmentArrayList = new ArrayList(objectGCScanningIterationCount); + for (int i = 0; i < objectGCScanningIterationCount; i++) { + AssortedValueWithObjectAlignment![] array = new AssortedValueWithObjectAlignment![genericArraySize]; + for (int j = 0; j < genericArraySize; j++) { + array[j] = AssortedValueWithObjectAlignment.createObjectWithDefaults(); + } + objectAlignmentArrayList.add(array); + } + + System.gc(); + + for (int i = 0; i < objectGCScanningIterationCount; i++) { + for (int j = 0; j < genericArraySize; j++) { + objectAlignmentArrayList.get(i)[j].checkFieldsWithDefaults(); + } + } + } + + /* + * Maintain a buffer of flattened arrays with single-aligned valuetypes while keeping a certain amount of classes alive at any + * single time. This forces the GC to unload the classes. + */ + @Test(priority=5, invocationCount=2) + static public void testValueWithSingleAlignmentGCScanning() throws Throwable { + ArrayList singleAlignmentArrayList = new ArrayList(objectGCScanningIterationCount); + for (int i = 0; i < objectGCScanningIterationCount; i++) { + AssortedValueWithSingleAlignment![] array = new AssortedValueWithSingleAlignment![genericArraySize]; + for (int j = 0; j < genericArraySize; j++) { + array[j] = AssortedValueWithSingleAlignment.createObjectWithDefaults(); + } + singleAlignmentArrayList.add(array); + } + + System.gc(); + + for (int i = 0; i < objectGCScanningIterationCount; i++) { + for (int j = 0; j < genericArraySize; j++) { + singleAlignmentArrayList.get(i)[j].checkFieldsWithDefaults(); + } + } + } + //******************************************************************************* // ACMP tests //******************************************************************************* @@ -1678,7 +2456,7 @@ static public void testMonitorEnterAndExitOnValueType() throws Throwable { @Test(priority=2) static public void testSynchMethodsOnValueTypes() throws Throwable { try { - ValueTypeGenerator.generateIllegalValueClassWithSychMethods("TestSynchMethodsOnValueTypesIllegal"); + ValueTypeGenerator.generateIllegalValueClassWithSynchMethods("TestSynchMethodsOnValueTypesIllegal", null); fail("should throw exception. Synchronized methods cannot be used with ValueType"); } catch (ClassFormatError e) {} @@ -1797,4 +2575,308 @@ static public void createClassWithVolatileValueTypeFields() throws Throwable { ClassWithValueTypeVolatile c = ClassWithValueTypeVolatile.createObjectWithDefaults(); c.checkFieldsWithDefaults(); } + + @Test(priority=1, expectedExceptions=ClassFormatError.class) + static public void testValueTypeHasSynchMethods() throws Throwable { + String fields[] = {"longField:J"}; + Class valueClass = ValueTypeGenerator.generateIllegalValueClassWithSynchMethods("TestValueTypeHasSynchMethods", fields); + } + + //******************************************************************************* + // default value tests + //******************************************************************************* + + /* + * Create a value type and read the fields before + * they are set. The test should Verify that the + * flattenable fields are set to the default values. + * NULL should never be observed for nullrestricted fields. + */ + @Test(priority=4) + static public void testDefaultValues() throws Throwable { + AssortedValueWithLongAlignment assortedValueWithLongAlignment = new AssortedValueWithLongAlignment(); + assertNotNull(assortedValueWithLongAlignment.point); + assertNotNull(assortedValueWithLongAlignment.line); + assertNotNull(assortedValueWithLongAlignment.o); + assertNotNull(assortedValueWithLongAlignment.l); + assertNotNull(assortedValueWithLongAlignment.d); + assertNotNull(assortedValueWithLongAlignment.i); + assertNotNull(assortedValueWithLongAlignment.tri); + + /* Test with assorted ref object with long alignment */ + AssortedRefWithLongAlignment assortedRefWithLongAlignment = new AssortedRefWithLongAlignment(); + assertNotNull(assortedRefWithLongAlignment.point); + assertNotNull(assortedRefWithLongAlignment.line); + assertNotNull(assortedRefWithLongAlignment.o); + assertNotNull(assortedRefWithLongAlignment.l); + assertNotNull(assortedRefWithLongAlignment.d); + assertNotNull(assortedRefWithLongAlignment.i); + assertNotNull(assortedRefWithLongAlignment.tri); + + /* Test with flattened line 2D */ + FlattenedLine2D flattenedLine2D = new FlattenedLine2D(); + assertNotNull(flattenedLine2D.st); + assertNotNull(flattenedLine2D.en); + + /* Test with triangle 2D */ + Triangle2D triangle2D = new Triangle2D(); + assertNotNull(triangle2D.v1); + assertNotNull(triangle2D.v2); + assertNotNull(triangle2D.v3); + } + + /* + * Create Array Objects with Point Class without initialization + * The array should be set to a Default Value. + * + * TODO not sure if this array needs to be nullrestricted, currently the ri + * is not up to date on this. Revisit this later. + */ + @Test(priority=4, invocationCount=2) + static public void testDefaultValueInPointArray() throws Throwable { + Point2D[] pointArray = new Point2D[genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + assertNotNull(pointArray[i]); + } + } + + /** + * Create multi dimensional array with Point Class without initialization. + * Set each array value to a default value and check field access method handler. + */ + @Test(priority=4) + static public void testDefaultValueInPointInstanceMultiArray() throws Throwable { + Point2D[][] pointArray = new Point2D[genericArraySize][genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + pointArray[i][j] = new Point2D(defaultPointPositions1); + } + } + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + pointArray[i][j].checkFieldAccessWithDefaultValues(); + } + } + } + + /* + * Create Array Objects with Flattened Line without initialization + * Check the fields of each element in arrays. No field should be NULL. + */ + @Test(priority=4, invocationCount=2) + static public void testDefaultValueInLineArray() throws Throwable { + FlattenedLine2D[] flattenedLineArray = new FlattenedLine2D[genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + FlattenedLine2D lineObject = flattenedLineArray[i]; + assertNotNull(lineObject); + assertNotNull(lineObject.st); + assertNotNull(lineObject.en); + } + } + + /** + * Create multi dimensional array with Line Class without initialization. + * Set each array value to a default value and check field access method handler. + */ + @Test(priority=4) + static public void testDefaultValueInLineInstanceMultiArray() throws Throwable { + FlattenedLine2D[][] flattenedLineArray = new FlattenedLine2D[genericArraySize][genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + FlattenedLine2D lineNew = new FlattenedLine2D(defaultLinePositions1); + flattenedLineArray[i][j] = lineNew; + } + } + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + flattenedLineArray[i][j].checkFieldAccessWithDefaultValues(); + } + } + } + + /* + * Create Array Objects with triangle class without initialization + * Check the fields of each element in arrays. No field should be NULL. + */ + @Test(priority=4, invocationCount=2) + static public void testDefaultValueInTriangleArray() throws Throwable { + Triangle2D[] triangleArray = new Triangle2D[genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + Triangle2D triangleObject = triangleArray[i]; + assertNotNull(triangleObject); + assertNotNull(triangleObject.v1); + assertNotNull(triangleObject.v2); + assertNotNull(triangleObject.v3); + } + } + + /** + * Create multi dimensional array with Triangle Class without initialization. + * Set each array value to a default value and check field access method handler. + */ + @Test(priority=4) + static public void testDefaultValueInTriangleInstanceMultiArray() throws Throwable { + Triangle2D[][] triangleArray = new Triangle2D[genericArraySize][genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + triangleArray[i][j] = new Triangle2D(defaultTrianglePositions); + } + } + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + triangleArray[i][j].checkFieldAccessWithDefaultValues(); + } + } + } + + /* + * Create an assortedRefWithLongAlignment Array + * Since it's ref type, the array should be filled with nullptrs + */ + @Test(priority=4, invocationCount=2) + static public void testDefaultValueInAssortedRefWithLongAlignmentArray() throws Throwable { + AssortedRefWithLongAlignment[] array = new AssortedRefWithLongAlignment[genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + assertNull(array[i]); + } + } + + /* + * Create an Array Object with assortedValueWithLongAlignment class without initialization + * Check the fields of each element in arrays. No field should be NULL. + */ + @Test(priority=4, invocationCount=2) + static public void testDefaultValueInAssortedValueWithLongAlignmentArray() throws Throwable { + AssortedValueWithLongAlignment[] array = new AssortedValueWithLongAlignment[genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + AssortedValueWithLongAlignment v = array[i]; + assertNotNull(v); + assertNotNull(v.point); + assertNotNull(v.line); + assertNotNull(v.o); + assertNotNull(v.l); + assertNotNull(v.d); + assertNotNull(v.i); + assertNotNull(v.tri); + } + } + + /** + * Create multi dimensional array with assortedValueWithLongAlignment without initialization. + * Set each array value to a default value and check field access method handler. + * + * Fails tests with array flattening enabled + */ + @Test(priority=5) + static public void testDefaultValueInAssortedValueWithLongAlignmenInstanceMultiArray() throws Throwable { + AssortedValueWithLongAlignment[][] array = new AssortedValueWithLongAlignment[genericArraySize][genericArraySize]; + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + array[i][j] = AssortedValueWithLongAlignment.createObjectWithDefaults(); + } + } + for (int i = 0; i < genericArraySize; i++) { + for (int j = 0; j < genericArraySize; j++) { + array[i][j].checkFieldsWithDefaults(); + } + } + } + + /* + * Create a 2D array of valueTypes, verify that the default elements are null. + */ + @Test(priority=5, invocationCount=2) + static public void testMultiDimentionalArrays() throws Throwable { + Class assortedValueWithLongAlignment2DClass = Array.newInstance(AssortedValueWithLongAlignment.class, 1).getClass(); + Class assortedValueWithSingleAlignment2DClass = Array.newInstance(AssortedValueWithSingleAlignment.class, 1).getClass(); + + Object assortedRefWithLongAlignment2DArray = Array.newInstance(assortedValueWithLongAlignment2DClass, genericArraySize); + Object assortedRefWithSingleAlignment2DArray = Array.newInstance(assortedValueWithSingleAlignment2DClass, genericArraySize); + + for (int i = 0; i < genericArraySize; i++) { + Object ref = Array.get(assortedRefWithLongAlignment2DArray, i); + assertNull(ref); + + ref = Array.get(assortedRefWithSingleAlignment2DArray, i); + assertNull(ref); + } + } + + @Test(priority=1) + static public void testCyclicalStaticFieldDefaultValues1() throws Throwable { + String cycleA1[] = { "val1:LCycleB1;:static" }; + String cycleB1[] = { "val1:LCycleA1;:static" }; + + Class cycleA1Class = ValueTypeGenerator.generateValueClass("CycleA1", cycleA1); + Class cycleB1Class = ValueTypeGenerator.generateValueClass("CycleB1", cycleB1); + + MethodHandle makeCycleA1 = lookup.findStatic(cycleA1Class, "makeValueGeneric", MethodType.methodType(Object.class)); + MethodHandle makeCycleB1 = lookup.findStatic(cycleB1Class, "makeValueGeneric", MethodType.methodType(Object.class)); + + makeCycleA1.invoke(); + makeCycleB1.invoke(); + } + + @Test(priority=1) + static public void testCyclicalStaticFieldDefaultValues2() throws Throwable { + String cycleA2[] = { "val1:LCycleB2;:static" }; + String cycleB2[] = { "val1:LCycleC2;:static" }; + String cycleC2[] = { "val1:LCycleA2;:static" }; + + Class cycleA2Class = ValueTypeGenerator.generateValueClass("CycleA2", cycleA2); + Class cycleB2Class = ValueTypeGenerator.generateValueClass("CycleB2", cycleB2); + Class cycleC2Class = ValueTypeGenerator.generateValueClass("CycleC2", cycleC2); + + MethodHandle makeCycleA2 = lookup.findStatic(cycleA2Class, "makeValueGeneric", MethodType.methodType(Object.class)); + MethodHandle makeCycleB2 = lookup.findStatic(cycleB2Class, "makeValueGeneric", MethodType.methodType(Object.class)); + MethodHandle makeCycleC2 = lookup.findStatic(cycleB2Class, "makeValueGeneric", MethodType.methodType(Object.class)); + + makeCycleA2.invoke(); + makeCycleB2.invoke(); + makeCycleC2.invoke(); + } + + @Test(priority=1) + static public void testCyclicalStaticFieldDefaultValues3() throws Throwable { + String cycleA3[] = { "val1:LCycleA3;:static" }; + + Class cycleA3Class = ValueTypeGenerator.generateValueClass("CycleA3", cycleA3); + + MethodHandle makeCycleA3 = lookup.findStatic(cycleA3Class, "makeValueGeneric", MethodType.methodType(Object.class)); + + makeCycleA3.invoke(); + } + + //******************************************************************************* + // checkcast tests + //******************************************************************************* + @Test(priority=1) + static public void testCheckCastValueTypeOnInvalidClass() throws Throwable { + String fields[] = {"longField:J"}; + Class valueClass = ValueTypeGenerator.generateValueClass("TestCheckCastOnInvalidClass", fields); + MethodHandle checkCastOnInvalidClass = lookup.findStatic(valueClass, "testCheckCastOnInvalidClass", MethodType.methodType(Object.class)); + checkCastOnInvalidClass.invoke(); + } + + /* + * Ensure that casting a non null value type to a valid value type will pass + */ + @Test(priority=1) + static public void testCheckCastValueTypeOnNonNullType() throws Throwable { + String fields[] = {"longField:J"}; + Class valueClass = ValueTypeGenerator.generateValueClass("TestCheckCastValueTypeOnNonNullType", fields); + MethodHandle checkCastValueTypeOnNonNullType = lookup.findStatic(valueClass, "testCheckCastValueTypeOnNonNullType", MethodType.methodType(Object.class)); + checkCastValueTypeOnNonNullType.invoke(); + } + + /* + * Ensure that casting null to a reference type class will pass + */ + @Test(priority=1) + static public void testCheckCastRefClassOnNull() throws Throwable { + String fields[] = {"longField:J"}; + Class refClass = ValueTypeGenerator.generateRefClass("TestCheckCastRefClassOnNull", fields); + MethodHandle checkCastRefClassOnNull = lookup.findStatic(refClass, "testCheckCastRefClassOnNull", MethodType.methodType(Object.class)); + checkCastRefClassOnNull.invoke(); + } }