Skip to content

Commit 17e1931

Browse files
committed
Instance strict field support (part 1)
This change includes support for strict instance fields except for the required stack map frame changes. A hash map is used to track that all strict instance fields are set prior to an <init> method's transition out of the early larval phase. Signed-off-by: Theresa Mammarella <[email protected]>
1 parent 78ceb9a commit 17e1931

File tree

9 files changed

+349
-18
lines changed

9 files changed

+349
-18
lines changed

runtime/bcverify/bcverify.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ static IDATA simulateStack (J9BytecodeVerificationData * verifyData);
8686
static IDATA parseOptions (J9JavaVM *vm, char *optionValues, const char **errorString);
8787
static IDATA setVerifyState ( J9JavaVM *vm, char *option, const char **errorString );
8888

89+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
90+
static UDATA strictFieldHashFn(void *key, void *userData);
91+
static UDATA strictFieldHashEqualFn(void *leftKey, void *rightKey, void *userData);
92+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
8993

9094
/**
9195
* Walk the J9-format stack maps and set the uninitialized_this flag appropriately
@@ -2302,9 +2306,12 @@ j9bcv_freeVerificationData (J9PortLibrary * portLib, J9BytecodeVerificationData
23022306
{
23032307
PORT_ACCESS_FROM_PORT(portLib);
23042308
if (verifyData) {
2309+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2310+
hashTableFree(verifyData->strictFields);
2311+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
23052312
#ifdef J9VM_THR_PREEMPTIVE
23062313
JavaVM* jniVM = (JavaVM*)verifyData->javaVM;
2307-
J9ThreadEnv* threadEnv;
2314+
J9ThreadEnv* threadEnv;
23082315
(*jniVM)->GetEnv(jniVM, (void**)&threadEnv, J9THREAD_VERSION_1_1);
23092316

23102317
threadEnv->monitor_destroy( verifyData->verifierMutex );
@@ -2344,6 +2351,22 @@ j9bcv_initializeVerificationData(J9JavaVM* javaVM)
23442351
goto error_no_memory;
23452352
}
23462353
#endif
2354+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2355+
verifyData->strictFieldsUnsetCount = 0;
2356+
verifyData->strictFields = hashTableNew(
2357+
OMRPORT_FROM_J9PORT(PORTLIB),
2358+
J9_GET_CALLSITE(),
2359+
0,
2360+
sizeof(J9StrictFieldEntry),
2361+
0, 0,
2362+
J9MEM_CATEGORY_CLASSES,
2363+
strictFieldHashFn,
2364+
strictFieldHashEqualFn,
2365+
NULL, javaVM);
2366+
if (NULL == verifyData->strictFields) {
2367+
goto error_no_memory;
2368+
}
2369+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
23472370

23482371
verifyData->verifyBytecodesFunction = j9bcv_verifyBytecodes;
23492372
verifyData->checkClassLoadingConstraintForNameFunction = j9bcv_checkClassLoadingConstraintForName;
@@ -2417,6 +2440,9 @@ j9bcv_verifyBytecodes (J9PortLibrary * portLib, J9Class * clazz, J9ROMClass * ro
24172440
BOOLEAN classVersionRequiresStackmaps = romClass->majorVersion >= CFR_MAJOR_VERSION_REQUIRING_STACKMAPS;
24182441
BOOLEAN newFormat = (classVersionRequiresStackmaps || hasStackMaps);
24192442
BOOLEAN verboseVerification = (J9_VERIFY_VERBOSE_VERIFICATION == (verifyData->verificationFlags & J9_VERIFY_VERBOSE_VERIFICATION));
2443+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2444+
BOOLEAN initMethodFound = TRUE;
2445+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
24202446

24212447
PORT_ACCESS_FROM_PORT(portLib);
24222448

@@ -2581,6 +2607,13 @@ j9bcv_verifyBytecodes (J9PortLibrary * portLib, J9Class * clazz, J9ROMClass * ro
25812607
ALWAYS_TRIGGER_J9HOOK_VM_METHOD_VERIFICATION_START(verifyData->javaVM->hookInterface, verifyData);
25822608
}
25832609

2610+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2611+
/* TODO Update the class version check if strict fields is released before value types. */
2612+
if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(romClass) && isInitMethod) {
2613+
createOrResetStrictFieldsList(verifyData, &initMethodFound);
2614+
}
2615+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
2616+
25842617
result = j9rtv_verifyBytecodes (verifyData);
25852618

25862619
if (BCV_ERR_INSUFFICIENT_MEMORY == result) {
@@ -2869,4 +2902,18 @@ bcvHookClassesUnload(J9HookInterface** hook, UDATA eventNum, void* eventData, vo
28692902
}
28702903
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
28712904

2905+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2906+
static UDATA strictFieldHashFn(void *key, void *userData) {
2907+
J9StrictFieldEntry *entry = key;
2908+
J9JavaVM *vm = userData;
2909+
return J9_VM_FUNCTION_VIA_JAVAVM(vm, computeHashForUTF8)(entry->name, entry->nameLength);
2910+
}
2911+
2912+
static UDATA strictFieldHashEqualFn(void *leftKey, void *rightKey, void *userData) {
2913+
J9StrictFieldEntry *leftEntry = leftKey;
2914+
J9StrictFieldEntry *rightEntry = rightKey;
2915+
return J9UTF8_DATA_EQUALS(leftEntry->name, leftEntry->nameLength, rightEntry->name, rightEntry->nameLength);
2916+
}
2917+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
2918+
28722919

runtime/bcverify/rtverify.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,9 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData)
471471
UDATA errorStackIndex = (UDATA)-1;
472472
UDATA errorTempData = (UDATA)-1;
473473
BOOLEAN isNextStack = FALSE;
474+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
475+
BOOLEAN anotherInstanceInitCalled = FALSE;
476+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
474477

475478
Trc_RTV_verifyBytecodes_Entry(verifyData->vmStruct,
476479
(UDATA) J9UTF8_LENGTH(J9ROMMETHOD_NAME(romMethod)),
@@ -1457,7 +1460,11 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData)
14571460
stackTop = pushFieldType(verifyData, utf8string, stackTop);
14581461
}
14591462

1460-
rc = isFieldAccessCompatible (verifyData, (J9ROMFieldRef *) info, bc, receiver, &reasonCode);
1463+
rc = isFieldAccessCompatible (verifyData, (J9ROMFieldRef *) info, bc, receiver, &reasonCode
1464+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
1465+
, isInitMethod, anotherInstanceInitCalled
1466+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
1467+
);
14611468
if (BCV_ERR_INSUFFICIENT_MEMORY == reasonCode) {
14621469
goto _outOfMemoryError;
14631470
}
@@ -1676,8 +1683,8 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData)
16761683
}
16771684
}
16781685

1679-
/* Ensure the <init> method is either for this class or its direct super class */
16801686
if (type & BCV_SPECIAL_INIT) {
1687+
/* Ensure the <init> method is either for this class or its direct super class. */
16811688
UDATA superClassIndex = 0;
16821689

16831690
utf8string = J9ROMSTRINGREF_UTF8DATA(classRef);
@@ -1695,6 +1702,24 @@ verifyBytecodes (J9BytecodeVerificationData * verifyData)
16951702
errorStackIndex = stackTop - liveStack->stackElements;
16961703
goto _inconsistentStack2;
16971704
}
1705+
1706+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
1707+
/* TODO Update the class version check if strict fields is released before value types. */
1708+
if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(verifyData->romClass)) {
1709+
if (classIndex == superClassIndex) {
1710+
/* All strict instance fields must be assigned before
1711+
* invoking the <init> method of a superclass.
1712+
*/
1713+
if (verifyData->strictFieldsUnsetCount > 0) {
1714+
errorType = J9NLS_BCV_ERR_BAD_INVOKESPECIAL__ID;
1715+
verboseErrorCode = BCV_ERR_BAD_INVOKESPECIAL;
1716+
goto _miscError;
1717+
}
1718+
} else {
1719+
anotherInstanceInitCalled = TRUE;
1720+
}
1721+
}
1722+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
16981723
}
16991724

17001725
/* This invoke special will make all copies of this "type" on the stack a real

runtime/bcverify/vrfyhelp.c

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -888,14 +888,27 @@ j9bcv_createVerifyErrorString(J9PortLibrary * portLib, J9BytecodeVerificationDat
888888
/*
889889
* Validates field access compatibility
890890
*
891+
* Update strictFields map for putfield
892+
* bytecode of strict final fields.
893+
*
891894
* returns TRUE if class are compatible
892895
* returns FALSE it not compatible
893896
* reasonCode (set by isClassCompatibleByName) is:
894897
* BCV_ERR_INSUFFICIENT_MEMORY :in OOM error case
895898
*/
896899
IDATA
897-
isFieldAccessCompatible(J9BytecodeVerificationData *verifyData, J9ROMFieldRef *fieldRef, UDATA bytecode, UDATA receiver, IDATA *reasonCode)
898-
{
900+
isFieldAccessCompatible(
901+
J9BytecodeVerificationData *verifyData,
902+
J9ROMFieldRef *fieldRef,
903+
UDATA bytecode,
904+
UDATA receiver,
905+
IDATA *reasonCode
906+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
907+
,
908+
BOOLEAN isInitMethod,
909+
BOOLEAN anotherInstanceInitCalled
910+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
911+
) {
899912
J9ROMClass *romClass = verifyData->romClass;
900913
J9ROMConstantPoolItem *constantPool = (J9ROMConstantPoolItem *)(romClass + 1);
901914
J9UTF8 *utf8string = J9ROMCLASSREF_NAME((J9ROMClassRef *)&constantPool[fieldRef->classRefCPIndex]);
@@ -907,14 +920,31 @@ isFieldAccessCompatible(J9BytecodeVerificationData *verifyData, J9ROMFieldRef *f
907920
J9ROMFieldShape *field = findFieldFromCurrentRomClass(romClass, fieldRef);
908921

909922
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
910-
/* A field declared by the current class with ACC_FINAL and ACC_STRICT flags
911-
* can't be set unless the initialization state is early larval.
912-
*/
913-
if ((NULL != field)
914-
&& J9ROMFIELD_IS_STRICT_FINAL(romClass, field->modifiers)
915-
&& (FALSE == liveStack->uninitializedThis)
923+
/* TODO Update the class version check if strict fields is released before value types. */
924+
if (J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(verifyData->romClass)
925+
&& (NULL != field) && J9ROMFIELD_IS_STRICT(field->modifiers)
916926
) {
917-
return (IDATA)FALSE;
927+
/* A strict final field cannot be set in the following cases:
928+
* - the initialization state is not early larval
929+
* - another instance initialization method has been called
930+
*/
931+
if (J9_ARE_ALL_BITS_SET(field->modifiers, J9AccFinal)
932+
&& ((FALSE == liveStack->uninitializedThis) || (TRUE == anotherInstanceInitCalled))
933+
) {
934+
return (IDATA)FALSE;
935+
}
936+
937+
if (isInitMethod && (TRUE == liveStack->uninitializedThis)) {
938+
J9UTF8 *fieldName = J9ROMNAMEANDSIGNATURE_NAME(J9ROMFIELDREF_NAMEANDSIGNATURE(fieldRef));
939+
J9StrictFieldEntry query = {0};
940+
query.name = J9UTF8_DATA(fieldName);
941+
query.nameLength = J9UTF8_LENGTH(fieldName);
942+
J9StrictFieldEntry *entry = hashTableFind(verifyData->strictFields, &query);
943+
if ((NULL != entry) && (FALSE == entry->isSet)) {
944+
entry->isSet = TRUE;
945+
verifyData->strictFieldsUnsetCount--;
946+
}
947+
}
918948
}
919949
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
920950

@@ -1259,3 +1289,46 @@ findFieldFromCurrentRomClass(J9ROMClass *romClass, J9ROMFieldRef *field)
12591289

12601290
return currentField;
12611291
}
1292+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
1293+
1294+
void
1295+
createOrResetStrictFieldsList(J9BytecodeVerificationData *verifyData, BOOLEAN *initMethodFound) {
1296+
if (*initMethodFound) {
1297+
*initMethodFound = FALSE;
1298+
1299+
/* Clear map entries from the last class. */
1300+
J9HashTableState hashTableState = {0};
1301+
J9StrictFieldEntry *entry = (J9StrictFieldEntry *)hashTableStartDo(verifyData->strictFields, &hashTableState);
1302+
while (NULL != entry) {
1303+
hashTableDoRemove(&hashTableState);
1304+
entry = hashTableNextDo(&hashTableState);
1305+
}
1306+
1307+
/* Create strictFields map of all strict instance fields. */
1308+
J9ROMFieldWalkState fieldWalkState = {0};
1309+
J9ROMFieldShape *field = romFieldsStartDo(verifyData->romClass, &fieldWalkState);
1310+
while (NULL != field) {
1311+
if (J9ROMFIELD_IS_STRICT(field->modifiers) && J9_ARE_NO_BITS_SET(field->modifiers, J9AccStatic)) {
1312+
J9UTF8 *fieldName = J9ROMFIELDSHAPE_NAME(field);
1313+
J9StrictFieldEntry newEntry = {0};
1314+
newEntry.name = J9UTF8_DATA(fieldName);
1315+
newEntry.nameLength = J9UTF8_LENGTH(fieldName);
1316+
newEntry.isSet = FALSE;
1317+
hashTableAdd(verifyData->strictFields, &newEntry);
1318+
}
1319+
field = romFieldsNextDo(&fieldWalkState);
1320+
}
1321+
} else {
1322+
/* Reset strictFields map and counter to be reused by another <init> method
1323+
* by marking all isSet flags as false.
1324+
*/
1325+
J9HashTableState hashTableState = {0};
1326+
J9StrictFieldEntry *entry = (J9StrictFieldEntry *)hashTableStartDo(verifyData->strictFields, &hashTableState);
1327+
while (NULL != entry) {
1328+
entry->isSet = FALSE;
1329+
entry = hashTableNextDo(&hashTableState);
1330+
}
1331+
}
1332+
verifyData->strictFieldsUnsetCount = hashTableGetCount(verifyData->strictFields);
1333+
}
1334+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */

runtime/oti/bcverify.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ typedef struct J9BranchTargetStack {
5353
UDATA stackElements[1];
5454
} J9BranchTargetStack;
5555

56+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
57+
typedef struct J9StrictFieldEntry {
58+
U_8 *name;
59+
UDATA nameLength;
60+
BOOLEAN isSet; /* isSet is not used for a query. */
61+
} J9StrictFieldEntry;
62+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
63+
5664
#define BCV_TARGET_STACK_HEADER_UDATA_SIZE 4
5765
#define BCV_STACK_OVERFLOW_BUFFER_UDATA_SIZE 2
5866

runtime/oti/bcverify_api.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,10 +414,26 @@ isClassCompatibleByName(J9BytecodeVerificationData *verifyData, UDATA sourceClas
414414
* @param receiver
415415
* @param reasonCode
416416
* output parameter denoting error conditions
417+
* @param isInitMethod
418+
* true if the current method is <init>
419+
* @param anotherInstanceInitCalled
420+
* true if the current method is <init> and another
421+
* instance <init> has been called in this method
417422
* @return IDATA
418423
*/
419424
IDATA
420-
isFieldAccessCompatible(J9BytecodeVerificationData * verifyData, J9ROMFieldRef * fieldRef, UDATA bytecode, UDATA receiver, IDATA *reasonCode);
425+
isFieldAccessCompatible(
426+
J9BytecodeVerificationData *verifyData,
427+
J9ROMFieldRef *fieldRef,
428+
UDATA bytecode,
429+
UDATA receiver,
430+
IDATA *reasonCode
431+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
432+
,
433+
BOOLEAN isInitMethod,
434+
BOOLEAN anotherInstanceInitCalled
435+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
436+
);
421437

422438
/**
423439
* @brief
@@ -494,6 +510,20 @@ pushLdcType(J9BytecodeVerificationData *verifyData, J9ROMClass * romClass, UDATA
494510
UDATA*
495511
pushReturnType(J9BytecodeVerificationData *verifyData, J9UTF8 * utf8string, UDATA * stackTop);
496512

513+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
514+
/**
515+
* Create or reset the list of strict instance fields for the class being
516+
* verified. This list is used to track which strict fields have been
517+
* set during the early larval state.
518+
*
519+
* @param verifyData information about the class being verified.
520+
* @param initMethodFound true if this is the first <init> found in the class, false otherwise.
521+
* @return void
522+
*/
523+
void
524+
createOrResetStrictFieldsList(J9BytecodeVerificationData *verifyData, BOOLEAN *initMethodFound);
525+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
526+
497527
/**
498528
* Classification of Unicode characters for use in Java identifiers.
499529
*/

runtime/oti/j9.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,7 @@ static const struct { \
373373
#define IS_CLASS_SIGNATURE(firstChar) ('L' == (firstChar))
374374

375375
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
376-
/* TODO Update the class version check if strict fields is released before value types. */
377-
#define J9ROMFIELD_IS_STRICT_FINAL(romClassOrClassfile, fieldModifiers) \
378-
(J9_IS_CLASSFILE_OR_ROMCLASS_VALUETYPE_VERSION(romClassOrClassfile) \
379-
&& J9_ARE_ALL_BITS_SET(fieldModifiers, J9AccStrictInit | J9AccFinal))
376+
#define J9ROMFIELD_IS_STRICT(fieldModifiers) J9_ARE_ALL_BITS_SET(fieldModifiers, J9AccStrictInit)
380377
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
381378

382379
#if defined(J9VM_OPT_CRIU_SUPPORT)

runtime/oti/j9nonbuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,10 @@ typedef struct J9BytecodeVerificationData {
21872187
struct J9PortLibrary * portLib;
21882188
struct J9JavaVM* javaVM;
21892189
BOOLEAN createdStackMap;
2190+
#if defined(J9VM_OPT_VALHALLA_STRICT_FIELDS)
2191+
J9HashTable *strictFields;
2192+
UDATA strictFieldsUnsetCount;
2193+
#endif /* defined(J9VM_OPT_VALHALLA_STRICT_FIELDS) */
21902194
} J9BytecodeVerificationData;
21912195

21922196
typedef struct J9BytecodeOffset {

0 commit comments

Comments
 (0)