diff --git a/runtime/bcverify/bcverify.c b/runtime/bcverify/bcverify.c index aa3905c6f06..488f51ed925 100644 --- a/runtime/bcverify/bcverify.c +++ b/runtime/bcverify/bcverify.c @@ -86,6 +86,10 @@ static IDATA simulateStack (J9BytecodeVerificationData * verifyData); static IDATA parseOptions (J9JavaVM *vm, char *optionValues, const char **errorString); static IDATA setVerifyState ( J9JavaVM *vm, char *option, const char **errorString ); +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) +static UDATA strictFieldHashFn(void *key, void *userData); +static UDATA strictFieldHashEqualFn(void *leftKey, void *rightKey, void *userData); +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ /** * Walk the J9-format stack maps and set the uninitialized_this flag appropriately @@ -2302,9 +2306,12 @@ j9bcv_freeVerificationData (J9PortLibrary * portLib, J9BytecodeVerificationData { PORT_ACCESS_FROM_PORT(portLib); if (verifyData) { +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + hashTableFree(verifyData->strictFields); +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ #ifdef J9VM_THR_PREEMPTIVE JavaVM* jniVM = (JavaVM*)verifyData->javaVM; - J9ThreadEnv* threadEnv; + J9ThreadEnv* threadEnv; (*jniVM)->GetEnv(jniVM, (void**)&threadEnv, J9THREAD_VERSION_1_1); threadEnv->monitor_destroy( verifyData->verifierMutex ); @@ -2344,6 +2351,22 @@ j9bcv_initializeVerificationData(J9JavaVM* javaVM) goto error_no_memory; } #endif +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + verifyData->strictFieldsUnsetCount = 0; + verifyData->strictFields = hashTableNew( + OMRPORT_FROM_J9PORT(PORTLIB), + J9_GET_CALLSITE(), + 0, + sizeof(J9StrictFieldEntry), + 0, 0, + J9MEM_CATEGORY_CLASSES, + strictFieldHashFn, + strictFieldHashEqualFn, + NULL, javaVM); + if (NULL == verifyData->strictFields) { + goto error_no_memory; + } +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ verifyData->verifyBytecodesFunction = j9bcv_verifyBytecodes; verifyData->checkClassLoadingConstraintForNameFunction = j9bcv_checkClassLoadingConstraintForName; @@ -2417,6 +2440,9 @@ j9bcv_verifyBytecodes (J9PortLibrary * portLib, J9Class * clazz, J9ROMClass * ro BOOLEAN classVersionRequiresStackmaps = romClass->majorVersion >= CFR_MAJOR_VERSION_REQUIRING_STACKMAPS; BOOLEAN newFormat = (classVersionRequiresStackmaps || hasStackMaps); BOOLEAN verboseVerification = (J9_VERIFY_VERBOSE_VERIFICATION == (verifyData->verificationFlags & J9_VERIFY_VERBOSE_VERIFICATION)); +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + BOOLEAN initMethodFound = TRUE; +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ PORT_ACCESS_FROM_PORT(portLib); @@ -2581,6 +2607,13 @@ j9bcv_verifyBytecodes (J9PortLibrary * portLib, J9Class * clazz, J9ROMClass * ro ALWAYS_TRIGGER_J9HOOK_VM_METHOD_VERIFICATION_START(verifyData->javaVM->hookInterface, verifyData); } +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + /* TODO Update the class version check if strict fields is released before value types. */ + if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(romClass) && isInitMethod) { + createOrResetStrictFieldsList(verifyData, &initMethodFound); + } +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ + result = j9rtv_verifyBytecodes (verifyData); if (BCV_ERR_INSUFFICIENT_MEMORY == result) { @@ -2869,4 +2902,18 @@ bcvHookClassesUnload(J9HookInterface** hook, UDATA eventNum, void* eventData, vo } #endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */ +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) +static UDATA strictFieldHashFn(void *key, void *userData) { + J9StrictFieldEntry *entry = key; + J9JavaVM *vm = userData; + return J9_VM_FUNCTION_VIA_JAVAVM(vm, computeHashForUTF8)(entry->name, entry->nameLength); +} + +static UDATA strictFieldHashEqualFn(void *leftKey, void *rightKey, void *userData) { + J9StrictFieldEntry *leftEntry = leftKey; + J9StrictFieldEntry *rightEntry = rightKey; + return J9UTF8_DATA_EQUALS(leftEntry->name, leftEntry->nameLength, rightEntry->name, rightEntry->nameLength); +} +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ + diff --git a/runtime/bcverify/rtverify.c b/runtime/bcverify/rtverify.c index 20800f9ff4a..02fa1de3ceb 100644 --- a/runtime/bcverify/rtverify.c +++ b/runtime/bcverify/rtverify.c @@ -471,6 +471,9 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData) UDATA errorStackIndex = (UDATA)-1; UDATA errorTempData = (UDATA)-1; BOOLEAN isNextStack = FALSE; +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + BOOLEAN anotherInstanceInitCalled = FALSE; +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ Trc_RTV_verifyBytecodes_Entry(verifyData->vmStruct, (UDATA) J9UTF8_LENGTH(J9ROMMETHOD_NAME(romMethod)), @@ -1457,7 +1460,11 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData) stackTop = pushFieldType(verifyData, utf8string, stackTop); } - rc = isFieldAccessCompatible (verifyData, (J9ROMFieldRef *) info, bc, receiver, &reasonCode); + rc = isFieldAccessCompatible (verifyData, (J9ROMFieldRef *) info, bc, receiver, &reasonCode +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + , isInitMethod, anotherInstanceInitCalled +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ + ); if (BCV_ERR_INSUFFICIENT_MEMORY == reasonCode) { goto _outOfMemoryError; } @@ -1676,8 +1683,8 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData) } } - /* Ensure the method is either for this class or its direct super class */ if (type & BCV_SPECIAL_INIT) { + /* Ensure the method is either for this class or its direct super class. */ UDATA superClassIndex = 0; utf8string = J9ROMSTRINGREF_UTF8DATA(classRef); @@ -1695,6 +1702,24 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData) errorStackIndex = stackTop - liveStack->stackElements; goto _inconsistentStack2; } + +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + /* TODO Update the class version check if strict fields is released before value types. */ + if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(verifyData->romClass)) { + if (classIndex == superClassIndex) { + /* All strict instance fields must be assigned before + * invoking the method of a superclass. + */ + if (verifyData->strictFieldsUnsetCount > 0) { + errorType = J9NLS_BCV_ERR_BAD_INVOKESPECIAL__ID; + verboseErrorCode = BCV_ERR_BAD_INVOKESPECIAL; + goto _miscError; + } + } else { + anotherInstanceInitCalled = TRUE; + } + } +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ } /* This invoke special will make all copies of this "type" on the stack a real diff --git a/runtime/bcverify/vrfyhelp.c b/runtime/bcverify/vrfyhelp.c index c96fdc91b65..ad59d36acbf 100644 --- a/runtime/bcverify/vrfyhelp.c +++ b/runtime/bcverify/vrfyhelp.c @@ -888,14 +888,27 @@ j9bcv_createVerifyErrorString(J9PortLibrary * portLib, J9BytecodeVerificationDat /* * Validates field access compatibility * + * Update strictFields map for putfield + * bytecode of strict final fields. + * * returns TRUE if class are compatible * returns FALSE it not compatible * reasonCode (set by isClassCompatibleByName) is: * BCV_ERR_INSUFFICIENT_MEMORY :in OOM error case */ IDATA -isFieldAccessCompatible(J9BytecodeVerificationData *verifyData, J9ROMFieldRef *fieldRef, UDATA bytecode, UDATA receiver, IDATA *reasonCode) -{ +isFieldAccessCompatible( + J9BytecodeVerificationData *verifyData, + J9ROMFieldRef *fieldRef, + UDATA bytecode, + UDATA receiver, + IDATA *reasonCode +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + , + BOOLEAN isInitMethod, + BOOLEAN anotherInstanceInitCalled +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ +) { J9ROMClass *romClass = verifyData->romClass; J9ROMConstantPoolItem *constantPool = (J9ROMConstantPoolItem *)(romClass + 1); J9UTF8 *utf8string = J9ROMCLASSREF_NAME((J9ROMClassRef *)&constantPool[fieldRef->classRefCPIndex]); @@ -907,14 +920,31 @@ isFieldAccessCompatible(J9BytecodeVerificationData *verifyData, J9ROMFieldRef *f J9ROMFieldShape *field = findFieldFromCurrentRomClass(romClass, fieldRef); #if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) - /* A field declared by the current class with ACC_FINAL and ACC_STRICT flags - * can't be set unless the initialization state is early larval. - */ - if ((NULL != field) - && J9ROMFIELD_IS_STRICT_FINAL(romClass, field->modifiers) - && (FALSE == liveStack->uninitializedThis) + /* TODO Update the class version check if strict fields is released before value types. */ + if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(verifyData->romClass) + && (NULL != field) && J9ROMFIELD_IS_STRICT(field->modifiers) ) { - return (IDATA)FALSE; + /* A strict final field cannot be set in the following cases: + * - the initialization state is not early larval + * - another instance initialization method has been called + */ + if (J9_ARE_ALL_BITS_SET(field->modifiers, J9AccFinal) + && ((FALSE == liveStack->uninitializedThis) || (TRUE == anotherInstanceInitCalled)) + ) { + return (IDATA)FALSE; + } + + if (isInitMethod && (TRUE == liveStack->uninitializedThis)) { + J9UTF8 *fieldName = J9ROMNAMEANDSIGNATURE_NAME(J9ROMFIELDREF_NAMEANDSIGNATURE(fieldRef)); + J9StrictFieldEntry query = {0}; + query.name = J9UTF8_DATA(fieldName); + query.nameLength = J9UTF8_LENGTH(fieldName); + J9StrictFieldEntry *entry = hashTableFind(verifyData->strictFields, &query); + if ((NULL != entry) && (FALSE == entry->isSet)) { + entry->isSet = TRUE; + verifyData->strictFieldsUnsetCount--; + } + } } #endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ @@ -1259,3 +1289,46 @@ findFieldFromCurrentRomClass(J9ROMClass *romClass, J9ROMFieldRef *field) return currentField; } +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + +void +createOrResetStrictFieldsList(J9BytecodeVerificationData *verifyData, BOOLEAN *initMethodFound) { + if (*initMethodFound) { + *initMethodFound = FALSE; + + /* Clear map entries from the last class. */ + J9HashTableState hashTableState = {0}; + J9StrictFieldEntry *entry = (J9StrictFieldEntry *)hashTableStartDo(verifyData->strictFields, &hashTableState); + while (NULL != entry) { + hashTableDoRemove(&hashTableState); + entry = hashTableNextDo(&hashTableState); + } + + /* Create strictFields map of all strict instance fields. */ + J9ROMFieldWalkState fieldWalkState = {0}; + J9ROMFieldShape *field = romFieldsStartDo(verifyData->romClass, &fieldWalkState); + while (NULL != field) { + if (J9ROMFIELD_IS_STRICT(field->modifiers) && J9_ARE_NO_BITS_SET(field->modifiers, J9AccStatic)) { + J9UTF8 *fieldName = J9ROMFIELDSHAPE_NAME(field); + J9StrictFieldEntry newEntry = {0}; + newEntry.name = J9UTF8_DATA(fieldName); + newEntry.nameLength = J9UTF8_LENGTH(fieldName); + newEntry.isSet = FALSE; + hashTableAdd(verifyData->strictFields, &newEntry); + } + field = romFieldsNextDo(&fieldWalkState); + } + } else { + /* Reset strictFields map and counter to be reused by another method + * by marking all isSet flags as false. + */ + J9HashTableState hashTableState = {0}; + J9StrictFieldEntry *entry = (J9StrictFieldEntry *)hashTableStartDo(verifyData->strictFields, &hashTableState); + while (NULL != entry) { + entry->isSet = FALSE; + entry = hashTableNextDo(&hashTableState); + } + } + verifyData->strictFieldsUnsetCount = hashTableGetCount(verifyData->strictFields); +} +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ \ No newline at end of file diff --git a/runtime/oti/bcverify.h b/runtime/oti/bcverify.h index 858a6d4325a..8d45cff0a22 100644 --- a/runtime/oti/bcverify.h +++ b/runtime/oti/bcverify.h @@ -53,6 +53,14 @@ typedef struct J9BranchTargetStack { UDATA stackElements[1]; } J9BranchTargetStack; +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) +typedef struct J9StrictFieldEntry { + U_8 *name; + UDATA nameLength; + BOOLEAN isSet; /* isSet is not used for a query. */ +} J9StrictFieldEntry; +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ + #define BCV_TARGET_STACK_HEADER_UDATA_SIZE 4 #define BCV_STACK_OVERFLOW_BUFFER_UDATA_SIZE 2 diff --git a/runtime/oti/bcverify_api.h b/runtime/oti/bcverify_api.h index efc48eb815e..c7a5189d328 100644 --- a/runtime/oti/bcverify_api.h +++ b/runtime/oti/bcverify_api.h @@ -414,10 +414,26 @@ isClassCompatibleByName(J9BytecodeVerificationData *verifyData, UDATA sourceClas * @param receiver * @param reasonCode * output parameter denoting error conditions +* @param isInitMethod +* true if the current method is +* @param anotherInstanceInitCalled +* true if the current method is and another +* instance has been called in this method * @return IDATA */ IDATA -isFieldAccessCompatible(J9BytecodeVerificationData * verifyData, J9ROMFieldRef * fieldRef, UDATA bytecode, UDATA receiver, IDATA *reasonCode); +isFieldAccessCompatible( + J9BytecodeVerificationData *verifyData, + J9ROMFieldRef *fieldRef, + UDATA bytecode, + UDATA receiver, + IDATA *reasonCode +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + , + BOOLEAN isInitMethod, + BOOLEAN anotherInstanceInitCalled +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ +); /** * @brief @@ -494,6 +510,20 @@ pushLdcType(J9BytecodeVerificationData *verifyData, J9ROMClass * romClass, UDATA UDATA* pushReturnType(J9BytecodeVerificationData *verifyData, J9UTF8 * utf8string, UDATA * stackTop); +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) +/** + * Create or reset the list of strict instance fields for the class being + * verified. This list is used to track which strict fields have been + * set during the early larval state. + * + * @param verifyData information about the class being verified. + * @param initMethodFound true if this is the first found in the class, false otherwise. + * @return void + */ +void +createOrResetStrictFieldsList(J9BytecodeVerificationData *verifyData, BOOLEAN *initMethodFound); +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ + /** * Classification of Unicode characters for use in Java identifiers. */ diff --git a/runtime/oti/j9.h b/runtime/oti/j9.h index 4243ccde9ac..c92fd73975e 100644 --- a/runtime/oti/j9.h +++ b/runtime/oti/j9.h @@ -373,10 +373,7 @@ static const struct { \ #define IS_CLASS_SIGNATURE(firstChar) ('L' == (firstChar)) #if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) -/* TODO Update the class version check if strict fields is released before value types. */ -#define J9ROMFIELD_IS_STRICT_FINAL(romClassOrClassfile, fieldModifiers) \ - (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(romClassOrClassfile) \ - && J9_ARE_ALL_BITS_SET(fieldModifiers, J9AccStrictInit | J9AccFinal)) +#define J9ROMFIELD_IS_STRICT(fieldModifiers) J9_ARE_ALL_BITS_SET(fieldModifiers, J9AccStrictInit) #endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ #if defined(J9VM_OPT_CRIU_SUPPORT) diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 64373d8d8dd..338fe9dd520 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -2187,6 +2187,10 @@ typedef struct J9BytecodeVerificationData { struct J9PortLibrary * portLib; struct J9JavaVM* javaVM; BOOLEAN createdStackMap; +#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) + J9HashTable *strictFields; + UDATA strictFieldsUnsetCount; +#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */ } J9BytecodeVerificationData; typedef struct J9BytecodeOffset { diff --git a/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldGenerator.java b/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldGenerator.java index 6d7776ed7b9..0f122f7ce3b 100644 --- a/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldGenerator.java +++ b/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldGenerator.java @@ -55,6 +55,9 @@ private static Class generateTestPutStrictFinalField(String className, boolea MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, className, fieldName, fieldDesc); + mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); if (lateLarval) { mv.visitVarInsn(ALOAD, 0); @@ -62,7 +65,7 @@ private static Class generateTestPutStrictFinalField(String className, boolea mv.visitFieldInsn(PUTFIELD, className, fieldName, fieldDesc); } mv.visitInsn(RETURN); - mv.visitMaxs(lateLarval ? 2 : 1, 1); + mv.visitMaxs(2, 1); mv.visitEnd(); if (unrestricted) { @@ -146,4 +149,109 @@ private static Class generateTestPutGetStrictStaticField(String className, bo return generator.defineClass(className, cw.toByteArray(), 0, bytes.length); } + + static Class generateTestInstanceStrictFieldSetBeforeSuperclassInit() { + String className = "TestInstanceStrictFieldSetBeforeSuperclassInit"; + return generateTestInstanceStrictField(className, true, false); + } + + static Class generateTestInstanceStrictFieldNotSetBeforeSuperclassInit() { + String className = "TestInstanceStrictFieldNotSetBeforeSuperclassInit"; + return generateTestInstanceStrictField(className, false, false); + } + + static Class generateTestInstanceStrictFieldNotSetBeforeSuperclassInitMulti() { + String className = "TestInstanceStrictFieldNotSetBeforeSuperclassInitMulti"; + return generateTestInstanceStrictField(className, true, true); + } + + private static Class generateTestInstanceStrictField(String className, boolean setField, boolean multipleFields) { + String fieldName = "i"; + String fieldDesc = "I"; + String fieldName2 = "i2"; + String fieldName3 = "i3"; + + ClassWriter cw = new ClassWriter(0); + cw.visit(ValhallaUtils.VALUE_TYPE_CLASS_FILE_VERSION, + ACC_PUBLIC + ValhallaUtils.ACC_IDENTITY, + className, null, "java/lang/Object", null); + + cw.visitField(ACC_STRICT_INIT, fieldName, fieldDesc, null, null); + if (multipleFields) { + cw.visitField(ACC_STRICT_INIT, fieldName2, fieldDesc, null, null); + cw.visitField(ACC_STRICT_INIT, fieldName3, fieldDesc, null, null); + } + + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + if (setField) { + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, className, fieldName, fieldDesc); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + int maxStack = 1; + if (setField) { + maxStack = 2; + } + mv.visitMaxs(maxStack, 1); + mv.visitEnd(); + + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + return generator.defineClass(className, cw.toByteArray(), 0, bytes.length); + } + + static Class testInstanceStrictFinalFieldSetAfterThisInit() { + String className = "TestInstanceStrictFinalFieldSetAfterThisInit"; + return generateTestInstanceStrictFieldThisInit(className, true); + } + + static Class generateTestInstanceStrictNonFinalFieldSetAfterThisInit() { + String className = "TestInstanceStrictNonFinalFieldSetAfterThisInit"; + return generateTestInstanceStrictFieldThisInit(className, false); + } + + private static Class generateTestInstanceStrictFieldThisInit(String className, boolean finalField) { + String fieldName = "i"; + String fieldDesc = "I"; + int fieldFlags = ACC_STRICT_INIT | (finalField ? ACC_FINAL : 0); + + ClassWriter cw = new ClassWriter(0); + cw.visit(ValhallaUtils.VALUE_TYPE_CLASS_FILE_VERSION, + ACC_PUBLIC + ValhallaUtils.ACC_IDENTITY, + className, null, "java/lang/Object", null); + + cw.visitField(fieldFlags, fieldName, fieldDesc, null, null); + + /* calls (I) to do the field initialization. */ + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitIntInsn(BIPUSH, 0); + mv.visitMethodInsn(INVOKESPECIAL, className, "", "(I)V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(BIPUSH, 1); + mv.visitFieldInsn(PUTFIELD, className, fieldName, fieldDesc); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + + /* (I) does the field initialization and then calls superclass's . */ + mv = cw.visitMethod(ACC_PUBLIC, "", "(I)V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitFieldInsn(PUTFIELD, className, fieldName, fieldDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + return generator.defineClass(className, cw.toByteArray(), 0, bytes.length); + } } diff --git a/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldTests.java b/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldTests.java index 7488bcd4851..eda30bec627 100644 --- a/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldTests.java +++ b/test/functional/Valhalla/src/org/openj9/test/lworld/StrictFieldTests.java @@ -100,4 +100,43 @@ static public void testStrictStaticFieldNotSetInLarvalMulti() throws Throwable { throw e.getException(); } } + + /* A strict instance field must be assigned by putfield before invoking an + * instance initialization method of the direct superclass. + */ + @Test + static public void testInstanceStrictFieldSetBeforeSuperclassInit() throws Throwable { + Class c = StrictFieldGenerator.generateTestInstanceStrictFieldSetBeforeSuperclassInit(); + c.newInstance(); + } + + @Test(expectedExceptions = VerifyError.class, expectedExceptionsMessageRegExp = ".*.*JBinvokespecial.*") + static public void testInstanceStrictFieldNotSetBeforeSuperclassInit() throws Throwable { + Class c = StrictFieldGenerator.generateTestInstanceStrictFieldNotSetBeforeSuperclassInit(); + c.newInstance(); + } + + @Test(expectedExceptions = VerifyError.class, expectedExceptionsMessageRegExp = ".*.*JBinvokespecial.*") + static public void testInstanceStrictFieldNotSetBeforeSuperclassInitMulti() throws Throwable { + Class c = StrictFieldGenerator.generateTestInstanceStrictFieldNotSetBeforeSuperclassInitMulti(); + c.newInstance(); + } + + /* A strict final instance field may not be assigned after invoking + * another instance initialization method of this. + */ + @Test(expectedExceptions = VerifyError.class, expectedExceptionsMessageRegExp = ".*.*JBputfield.*") + static public void testInstanceStrictFinalFieldSetAfterThisInit() throws Throwable { + Class c = StrictFieldGenerator.testInstanceStrictFinalFieldSetAfterThisInit(); + c.newInstance(); + } + + /* A non-final strict instance field may be assigned after invoking + * another instance initialization method of this. + */ + @Test + static public void testInstanceStrictNonFinalFieldSetAfterThisInit() throws Throwable { + Class c = StrictFieldGenerator.generateTestInstanceStrictNonFinalFieldSetAfterThisInit(); + c.newInstance(); + } }