Skip to content

Commit

Permalink
Merge pull request #18030 from theresa-m/nr_verification_3
Browse files Browse the repository at this point in the history
NullRestricted attribute field class checks
  • Loading branch information
hangshao0 committed Sep 21, 2023
2 parents da175cf + b4bc9cd commit 27f0069
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 17 deletions.
16 changes: 16 additions & 0 deletions runtime/nls/j9vm/j9vm.nls
Expand Up @@ -2175,3 +2175,19 @@ J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARAL
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.system_action=The JVM will throw a JVMRestoreException.
J9NLS_VM_CRIU_RESTORE_RESET_VIRTUALTHREAD_FORKJOINPOOL_PARALLELISM_INVALID_PARALLELISM_OFFSET.user_response=View CRIU documentation to determine how to resolve the exception.
# END NON-TRANSLATABLE

# NullRestricted/value/implicit is not translatable
J9NLS_VM_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS=An instance field with a NullRestricted attribute must be in a value class with an implicit constructor
# START NON-TRANSLATABLE
J9NLS_VM_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.explanation=Please consult the Java Virtual Machine Specification for a detailed explanation.
J9NLS_VM_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.system_action=The JVM will throw a verification or classloading related exception such as java.lang.ClassFormatError.
J9NLS_VM_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.user_response=Contact the provider of the classfile for a corrected version.
# END NON-TRANSLATABLE

# NullRestricted/value/implicit is not translatable
J9NLS_VM_STATIC_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS=A static field with a NullRestricted attribute must be in a value class with an implicit constructor
# START NON-TRANSLATABLE
J9NLS_VM_STATIC_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.explanation=Please consult the Java Virtual Machine Specification for a detailed explanation.
J9NLS_VM_STATIC_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.system_action=The JVM will throw a verification or classloading related exception such as java.lang.ClassFormatError.
J9NLS_VM_STATIC_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS.user_response=Contact the provider of the classfile for a corrected version.
# END NON-TRANSLATABLE
6 changes: 4 additions & 2 deletions runtime/oti/j9javaaccessflags.h
Expand Up @@ -101,8 +101,6 @@
* See map in ROMClassBuilder::computeExtraModifiers for
* available slots.
*/
#define J9AccImplicitCreateHasDefaultValue 0x10
#define J9AccImplicitCreateNonAtomic 0x20
#define J9AccClassIsValueBased 0x40
#define J9AccClassHiddenOptionNestmate 0x80
#define J9AccClassHiddenOptionStrong 0x100
Expand Down Expand Up @@ -167,4 +165,8 @@
#define J9StaticFieldRefFlagBits 0x3F
#endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */

/* ImplicitCreation attribute flags */
#define J9AccImplicitCreateHasDefaultValue 0x1
#define J9AccImplicitCreateNonAtomic 0x2

#endif /*J9JAVAACCESSFLAGS_H */
17 changes: 17 additions & 0 deletions runtime/vm/ClassInitialization.cpp
Expand Up @@ -33,6 +33,7 @@
#include "VMHelpers.hpp"
#include "AtomicSupport.hpp"
#include "ObjectMonitor.hpp"
#include "util_api.h"

extern "C" {

Expand Down Expand Up @@ -600,6 +601,22 @@ classInitStateMachine(J9VMThread *currentThread, J9Class *clazz, J9ClassInitStat
bool isStatic = J9_VM_FCC_ENTRY_IS_STATIC_FIELD(entry);

if (isStatic) {
U_32 fieldModifiers = entry->field->modifiers;
if (J9_ARE_ALL_BITS_SET(fieldModifiers, J9FieldFlagIsNullRestricted)) {
J9ROMClass *entryRomClass = entryClazz->romClass;
/* A NullRestricted field must be in a value class with an
* ImplicitCreation attribute. The attribute must have the ACC_DEFAULT flag set.
*/
if (J9_ARE_NO_BITS_SET(entryRomClass->modifiers, J9AccValueType)
|| J9_ARE_NO_BITS_SET(entryRomClass->optionalFlags, J9_ROMCLASS_OPTINFO_IMPLICITCREATION_ATTRIBUTE)
|| J9_ARE_NO_BITS_SET(getImplicitCreationFlags(entryRomClass), J9AccImplicitCreateHasDefaultValue)
) {
J9UTF8 *romClassName = J9ROMCLASS_CLASSNAME(entryRomClass);
setCurrentExceptionNLSWithArgs(currentThread, J9NLS_VM_STATIC_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS, J9VMCONSTANTPOOL_JAVALANGINCOMPATIBLECLASSCHANGEERROR, J9UTF8_LENGTH(romClassName), J9UTF8_DATA(romClassName));
goto done;
}
}

initializationLock = enterInitializationLock(currentThread, initializationLock);
if (J9_OBJECT_MONITOR_ENTER_FAILED(initializationLock)) {
goto done;
Expand Down
20 changes: 18 additions & 2 deletions runtime/vm/createramclass.cpp
Expand Up @@ -2022,7 +2022,7 @@ loadFlattenableFieldValueClasses(J9VMThread *currentThread, J9ClassLoader *class
break;
}
} else {
if ('Q' == signatureChars[0]) {
if ('Q' == signatureChars[0] || J9_ARE_ALL_BITS_SET(modifiers, J9FieldFlagIsNullRestricted)) {
J9FlattenedClassCacheEntry *entry = J9_VM_FCC_ENTRY_FROM_FCC(flattenedClassCache, flattenableFieldCount);
entry->clazz = (J9Class *) J9_VM_FCC_CLASS_FLAGS_STATIC_FIELD;
entry->field = field;
Expand Down Expand Up @@ -2065,7 +2065,9 @@ checkFlattenableFieldValueClasses(J9VMThread *currentThread, J9ClassLoader *clas
J9UTF8 *signature = J9ROMFIELDSHAPE_SIGNATURE(field);
U_8 *signatureChars = J9UTF8_DATA(signature);
if (J9_ARE_NO_BITS_SET(modifiers, J9AccStatic)) {
if ('Q' == signatureChars[0]) {
if ('Q' == signatureChars[0]
|| J9_ARE_ALL_BITS_SET(modifiers, J9FieldFlagIsNullRestricted)
) {
J9Class *valueClass = internalFindClassUTF8(currentThread, signatureChars + 1, J9UTF8_LENGTH(signature) - 2, classLoader, J9_FINDCLASS_FLAG_EXISTING_ONLY);
Assert_VM_notNull(valueClass);
J9ROMClass *valueROMClass = valueClass->romClass;
Expand All @@ -2081,6 +2083,20 @@ checkFlattenableFieldValueClasses(J9VMThread *currentThread, J9ClassLoader *clas
result = FALSE;
break;
}

/* A NullRestricted field must be in a value class with an
* ImplicitCreation attribute. The attribute must have the ACC_DEFAULT flag set.
* Static fields will be checked during class preparation.
*/
if (J9_ARE_ALL_BITS_SET(modifiers, J9FieldFlagIsNullRestricted)) {
if (J9_ARE_NO_BITS_SET(valueROMClass->modifiers, J9AccValueType)
|| J9_ARE_NO_BITS_SET(valueROMClass->optionalFlags, J9_ROMCLASS_OPTINFO_IMPLICITCREATION_ATTRIBUTE)
|| J9_ARE_NO_BITS_SET(getImplicitCreationFlags(valueROMClass), J9AccImplicitCreateHasDefaultValue)
) {
setCurrentExceptionForBadClass(currentThread, J9ROMCLASS_CLASSNAME(romClass), J9VMCONSTANTPOOL_JAVALANGINCOMPATIBLECLASSCHANGEERROR,
J9NLS_VM_NULLRESTRICTED_MUST_BE_IN_DEFAULT_IMPLICITCREATION_VALUE_CLASS);
}
}
}
}
field = romFieldsNextDo(&fieldWalkState);
Expand Down
Expand Up @@ -42,48 +42,48 @@ public static Class<?> generateClassWithPreloadAttribute(String name, String[] c

public static Class<?> generateClassWithTwoImplicitCreationAttributes(String name) throws Throwable {
byte[] bytes = generateClass(name, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE,
new Attribute[] {new ImplicitCreationAttribute(0), new ImplicitCreationAttribute(0)});
new Attribute[] {new ImplicitCreationAttribute(), new 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(0)});
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(0)});
new Attribute[] {new 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(ValhallaUtils.ACC_DEFAULT)});
new Attribute[] {new ImplicitCreationAttribute()});
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

/* Generate class with field and multiple NullRestricted attributes */
byte[] classBytes = generateClassWithField(className, ValhallaUtils.ACC_IDENTITY,
byte[] classBytes = generateIdentityClassWithField(className, 0,
"field", fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute(), new 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 = generateClassWithField(className, ValhallaUtils.ACC_IDENTITY,
byte[] classBytes = generateIdentityClassWithField(className, 0,
"field", "I", new Attribute[]{new 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(ValhallaUtils.ACC_DEFAULT)});
new Attribute[] {new ImplicitCreationAttribute()});
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

/* Generate class field field that is an array with a NullRestricted attribute */
byte[] classBytes = generateClassWithField(className, ValhallaUtils.ACC_IDENTITY,
byte[] classBytes = generateIdentityClassWithField(className, 0,
"field", "[" + fieldClass.descriptorString(), new Attribute[]{new NullRestrictedAttribute()});
return generator.defineClass(className, classBytes, 0, classBytes.length);
}
Expand All @@ -93,7 +93,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(ValhallaUtils.ACC_DEFAULT)});
new Attribute[] {new ImplicitCreationAttribute()});
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

ClassWriter classWriter = new ClassWriter(0);
Expand Down Expand Up @@ -131,7 +131,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(ValhallaUtils.ACC_DEFAULT)});
new Attribute[] {new ImplicitCreationAttribute()});
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

ClassWriter classWriter = new ClassWriter(0);
Expand All @@ -158,6 +158,40 @@ public static Class<?> generatePutFieldNullToNullRestrictedField(String classNam
return generator.defineClass(className, classBytes, 0, classBytes.length);
}

public static Class<?> generateNullRestrictedAttributeInIdentityClass(boolean isStatic, String className, String fieldClassName) throws Throwable {
/* Generate field class - identity class */
byte[] fieldClassBytes = generateClass(fieldClassName, ValhallaUtils.ACC_IDENTITY, null);
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

/* 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()});
return generator.defineClass(className, classBytes, 0, classBytes.length);
}

public static Class<?> generateNullRestrictedAttributeInValueClassWithoutIC(boolean isStatic, String className, String fieldClassName) throws Throwable {
/* Generate field class - value class with no attributes */
byte[] fieldClassBytes = generateClass(fieldClassName, ACC_FINAL + ValhallaUtils.ACC_VALUE_TYPE, null);
Class<?> fieldClass = generator.defineClass(fieldClassName, fieldClassBytes, 0, fieldClassBytes.length);

/* Generate class with field that has a NullRestricted attribute */
byte[] classBytes = generateIdentityClassWithField(className, isStatic ? ACC_STATIC : 0,
"field", fieldClass.descriptorString(), new Attribute[]{new 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)});
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()});
return generator.defineClass(className, classBytes, 0, classBytes.length);
}

public static Class<?> generateWithFieldStoreNullToNullRestrictedField(String className, String fieldClassName) {
String fieldName = "field";

Expand Down Expand Up @@ -212,17 +246,26 @@ public static byte[] generateClass(String name, int classFlags, Attribute[] attr
return classWriter.toByteArray();
}

public static byte[] generateClassWithField(String name, int classFlags, String fieldName, String fieldDescriptor, Attribute[] fieldAttributes) {
public static byte[] generateIdentityClassWithField(String name, int fieldFlags, String fieldName, String fieldDescriptor, Attribute[] fieldAttributes) {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(ValhallaUtils.CLASS_FILE_MAJOR_VERSION, classFlags, name, null, "java/lang/Object", null);
classWriter.visit(ValhallaUtils.CLASS_FILE_MAJOR_VERSION, ValhallaUtils.ACC_IDENTITY, name, null, "java/lang/Object", null);

FieldVisitor fieldVisitor = classWriter.visitField(0, fieldName, fieldDescriptor, null, null);
FieldVisitor fieldVisitor = classWriter.visitField(fieldFlags, fieldName, fieldDescriptor, null, null);
if (null != fieldAttributes) {
for (Attribute attr : fieldAttributes) {
fieldVisitor.visitAttribute(attr);
}
}

/* <init> */
MethodVisitor mvInit = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mvInit.visitCode();
mvInit.visitVarInsn(ALOAD, 0);
mvInit.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mvInit.visitInsn(RETURN);
mvInit.visitMaxs(1, 1);
mvInit.visitEnd();

classWriter.visitEnd();
return classWriter.toByteArray();
}
Expand Down Expand Up @@ -264,6 +307,12 @@ public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int
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;
Expand Down

0 comments on commit 27f0069

Please sign in to comment.