From 4ac4cdc8164d6563392b6c89814ff12b154a0d9b Mon Sep 17 00:00:00 2001
From: Graham Chapman <graham_chapman@ca.ibm.com>
Date: Fri, 20 Oct 2017 15:00:19 -0400
Subject: [PATCH] Fix exception creation during vTable init

Do not create exceptions while holding classTableMutex - instead, pass
the data out to be used after the appropriate locks have been released.

Signed-off-by: Graham Chapman <graham_chapman@ca.ibm.com>
---
 runtime/vm/createramclass.cpp | 77 ++++++++++++++++++++++++-----------
 1 file changed, 54 insertions(+), 23 deletions(-)

diff --git a/runtime/vm/createramclass.cpp b/runtime/vm/createramclass.cpp
index 201d890e09d..e7a95a4093e 100644
--- a/runtime/vm/createramclass.cpp
+++ b/runtime/vm/createramclass.cpp
@@ -118,14 +118,24 @@ typedef struct J9EquivalentEntry {
 	struct J9EquivalentEntry *next;
 } J9EquivalentEntry;
 
+typedef struct J9OverrideErrorData {
+	J9ClassLoader *loader1;
+	J9UTF8 *class1NameUTF;
+	J9ClassLoader *loader2;
+	J9UTF8 *class2NameUTF;
+	J9UTF8 *exceptionClassNameUTF;
+	J9UTF8 *methodNameUTF;
+	J9UTF8 *methodSigUTF;
+} J9OverrideErrorData;
+
 static J9Class* markInterfaces(J9ROMClass *romClass, J9Class *superclass, J9ClassLoader *classLoader, BOOLEAN *foundCloneable, UDATA *markedInterfaceCount, UDATA *inheritedInterfaceCount, IDATA *maxInterfaceDepth);
 static void unmarkInterfaces(J9Class *interfaceHead);
 static void createITable(J9VMThread* vmStruct, J9Class *ramClass, J9Class *interfaceClass, J9ITable ***previousLink, UDATA **currentSlot, UDATA depth);
 static UDATA* initializeRAMClassITable(J9VMThread* vmStruct, J9Class *ramClass, J9Class *superclass, UDATA* currentSlot, J9Class *interfaceHead, IDATA maxInterfaceDepth);
-static UDATA addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *interfaceClass, UDATA vTableWriteIndex, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, UDATA *defaultConflictCount, J9Pool *equivalentSets, UDATA *equivSetCount);
-static UDATA* computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *superclass, J9ROMClass *taggedClass, UDATA packageID, J9ROMMethod ** methodRemapArray, J9Class *interfaceHead, UDATA *defaultConflictCount, UDATA interfaceCount, UDATA inheritedInterfaceCount);
+static UDATA addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *interfaceClass, UDATA vTableWriteIndex, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, UDATA *defaultConflictCount, J9Pool *equivalentSets, UDATA *equivSetCount, J9OverrideErrorData *errorData);
+static UDATA* computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *superclass, J9ROMClass *taggedClass, UDATA packageID, J9ROMMethod ** methodRemapArray, J9Class *interfaceHead, UDATA *defaultConflictCount, UDATA interfaceCount, UDATA inheritedInterfaceCount, J9OverrideErrorData *errorData);
 static void copyVTable(J9VMThread *vmStruct, J9Class *ramClass, J9Class *superclass, UDATA *vTable, UDATA defaultConflictCount);
-static UDATA processVTableMethod(J9VMThread *vmThread, J9ClassLoader *classLoader, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, J9ROMMethod *romMethod, UDATA localPackageID, UDATA vTableWriteIndex, void *storeValue);
+static UDATA processVTableMethod(J9VMThread *vmThread, J9ClassLoader *classLoader, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, J9ROMMethod *romMethod, UDATA localPackageID, UDATA vTableWriteIndex, void *storeValue, J9OverrideErrorData *errorData);
 static VMINLINE UDATA growNewVTableSlot(UDATA *vTableAddress, UDATA vTableWriteIndex, void *storeValue);
 static UDATA getVTableIndexForNameAndSigStartingAt(UDATA *vTable, J9UTF8 *name, J9UTF8 *signature, UDATA vTableIndex);
 static UDATA checkPackageAccess(J9VMThread *vmThread, J9Class *foundClass, UDATA classPreloadFlags);
@@ -404,7 +414,7 @@ typedef enum {
 
 
 static UDATA
-addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *interfaceClass, UDATA vTableWriteIndex, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, UDATA *defaultConflictCount, J9Pool *equivalentSets, UDATA *equivSetCount)
+addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *interfaceClass, UDATA vTableWriteIndex, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, UDATA *defaultConflictCount, J9Pool *equivalentSets, UDATA *equivSetCount, J9OverrideErrorData *errorData)
 {
 	J9ROMClass *interfaceROMClass = interfaceClass->romClass;
 	UDATA count = interfaceROMClass->romMethodCount;
@@ -497,8 +507,8 @@ addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *i
 										/* Convert to equivSet by adding the existing vtable method to the equivSet */
 										J9EquivalentEntry *entry = (J9EquivalentEntry*) pool_newElement(equivalentSets);
 										if (NULL == entry) {
-											setNativeOutOfMemoryError(vmStruct, 0, 0);
-											goto done;
+											/* OOM will be thrown */
+											goto fail;
 										}
 										entry->method = (J9Method *)vTableAddress[tempIndex];
 										entry->next = NULL;
@@ -530,8 +540,8 @@ addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *i
 							existing_entry = previous_entry;
 							J9EquivalentEntry * new_entry = (J9EquivalentEntry*) pool_newElement(equivalentSets);
 							if (NULL == new_entry) {
-								setNativeOutOfMemoryError(vmStruct, 0, 0);
-								goto done;
+								/* OOM will be thrown */
+								goto fail;
 							}
 							new_entry->method = interfaceMethod;
 							new_entry->next = existing_entry->next;
@@ -559,9 +569,15 @@ addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *i
 										vTableMethodClassNameUTF = J9ROMCLASS_CLASSNAME(methodClass->romClass);
 									}
 									J9UTF8 *interfaceClassNameUTF = J9ROMCLASS_CLASSNAME(interfaceClass->romClass);
-									setClassLoadingConstraintOverrideError(vmStruct, J9ROMCLASS_CLASSNAME(romClass), vTableMethodLoader, vTableMethodClassNameUTF, interfaceLoader, interfaceClassNameUTF, interfaceClassNameUTF, J9UTF8_DATA(vTableMethodNameUTF), J9UTF8_LENGTH(vTableMethodNameUTF), J9UTF8_DATA(vTableMethodSigUTF), J9UTF8_LENGTH(vTableMethodSigUTF));
-									vTableWriteIndex = 0;
-									goto done;
+									/* LinkageError will be thrown */
+									errorData->loader1 = vTableMethodLoader;
+									errorData->class1NameUTF = vTableMethodClassNameUTF;
+									errorData->loader2 = interfaceLoader;
+									errorData->class2NameUTF = interfaceClassNameUTF;
+									errorData->exceptionClassNameUTF = interfaceClassNameUTF;
+									errorData->methodNameUTF = vTableMethodNameUTF;
+									errorData->methodSigUTF = vTableMethodSigUTF;
+									goto fail;
 								}
 
 							}
@@ -591,6 +607,9 @@ addInterfaceMethods(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *i
 	}
 done:
 	return vTableWriteIndex;
+fail:
+	vTableWriteIndex = 0;
+	goto done;
 }
 
 /*
@@ -667,7 +686,7 @@ continueProcessingVTable: ;
  * The caller must hold the class table mutex or exclusive access.
  */
 static UDATA *
-computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *superclass, J9ROMClass *taggedClass, UDATA packageID, J9ROMMethod ** methodRemapArray, J9Class *interfaceHead, UDATA *defaultConflictCount, UDATA interfaceCount, UDATA inheritedInterfaceCount)
+computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *superclass, J9ROMClass *taggedClass, UDATA packageID, J9ROMMethod ** methodRemapArray, J9Class *interfaceHead, UDATA *defaultConflictCount, UDATA interfaceCount, UDATA inheritedInterfaceCount, J9OverrideErrorData *errorData)
 {
 	J9JavaVM *vm = vmStruct->javaVM;
 	J9ROMClass *romClass = taggedClass;
@@ -786,7 +805,7 @@ computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *supercl
 					&& ('<' != J9UTF8_DATA(methodName)[0])
 					) {
 						vTableWriteIndex = processVTableMethod(vmStruct, classLoader, vTableAddress, superclass, romClass, romMethod,
-								packageID, vTableWriteIndex, (J9ROMMethod *)((UDATA)romMethod + ROM_METHOD_ID_TAG));
+								packageID, vTableWriteIndex, (J9ROMMethod *)((UDATA)romMethod + ROM_METHOD_ID_TAG), errorData);
 						if (0 == vTableWriteIndex) {
 							goto fail;
 						}
@@ -849,7 +868,7 @@ computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *supercl
 #endif /* VERBOSE_INTERFACE_METHODS */
 				J9Pool *equivalentSet = pool_new(sizeof(J9EquivalentEntry),  0, 0, 0, J9_GET_CALLSITE(), J9MEM_CATEGORY_CLASSES, POOL_FOR_PORT(vm->portLibrary));
 				if (NULL == equivalentSet) {
-					setNativeOutOfMemoryError(vmStruct, 0, 0);
+					/* OOM will be thrown */
 					goto fail;
 				}
 				UDATA equivSetCount = 0;
@@ -860,7 +879,7 @@ computeVTable(J9VMThread *vmStruct, J9ClassLoader *classLoader, J9Class *supercl
 						j9tty_printf(PORTLIB, "\n\t<%.*s>", J9UTF8_LENGTH(className), J9UTF8_DATA(className));
 					}
 #endif /* VERBOSE_INTERFACE_METHODS */
-					vTableWriteIndex = addInterfaceMethods(vmStruct, classLoader, interfaces[i - 1], vTableWriteIndex, vTableAddress, superclass, romClass, defaultConflictCount, equivalentSet, &equivSetCount);
+					vTableWriteIndex = addInterfaceMethods(vmStruct, classLoader, interfaces[i - 1], vTableWriteIndex, vTableAddress, superclass, romClass, defaultConflictCount, equivalentSet, &equivSetCount, errorData);
 					if (0 == vTableWriteIndex) {
 						if (NULL != equivalentSet) {
 							pool_kill(equivalentSet);
@@ -1120,7 +1139,7 @@ fillJITVTableSlot(J9VMThread *vmStruct, UDATA *currentSlot, J9Method *currentMet
 #endif
 
 static UDATA
-processVTableMethod(J9VMThread *vmThread, J9ClassLoader *classLoader, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, J9ROMMethod *romMethod, UDATA localPackageID, UDATA vTableWriteIndex, void *storeValue)
+processVTableMethod(J9VMThread *vmThread, J9ClassLoader *classLoader, UDATA *vTableAddress, J9Class *superclass, J9ROMClass *romClass, J9ROMMethod *romMethod, UDATA localPackageID, UDATA vTableWriteIndex, void *storeValue, J9OverrideErrorData *errorData)
 {
 	BOOLEAN anyOverrides = FALSE;
 	J9UTF8 *nameUTF = J9ROMMETHOD_NAME(romMethod);
@@ -1174,7 +1193,13 @@ processVTableMethod(J9VMThread *vmThread, J9ClassLoader *classLoader, UDATA *vTa
 										J9UTF8 *superclassVTableMethodClassNameUTF = J9ROMCLASS_CLASSNAME(superclassVTableMethodClass->romClass);
 										J9UTF8 *newClassNameUTF = J9ROMCLASS_CLASSNAME(romClass);
 										J9UTF8 *superclassVTableMethodNameUTF = J9ROMMETHOD_NAME(superclassVTableROMMethod);
-										setClassLoadingConstraintOverrideError(vmThread, J9ROMCLASS_CLASSNAME(romClass), classLoader, newClassNameUTF, superclassVTableMethodLoader, superclassVTableMethodClassNameUTF, superclassVTableMethodClassNameUTF, J9UTF8_DATA(superclassVTableMethodNameUTF), J9UTF8_LENGTH(superclassVTableMethodNameUTF), J9UTF8_DATA(superclassVTableMethodSigUTF), J9UTF8_LENGTH(superclassVTableMethodSigUTF));
+										errorData->loader1 = classLoader;
+										errorData->class1NameUTF = newClassNameUTF;
+										errorData->loader2 = superclassVTableMethodLoader;
+										errorData->class2NameUTF = superclassVTableMethodClassNameUTF;
+										errorData->exceptionClassNameUTF = superclassVTableMethodClassNameUTF;
+										errorData->methodNameUTF = superclassVTableMethodNameUTF;
+										errorData->methodSigUTF = superclassVTableMethodSigUTF;
 										vTableWriteIndex = 0;
 										goto done;
 									}
@@ -1862,6 +1887,7 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
 	UDATA inheritedInterfaceCount = 0;
 	UDATA defaultConflictCount = 0;
 	UDATA length = 0;
+	J9OverrideErrorData errorData = {0};
 
 	PORT_ACCESS_FROM_JAVAVM(javaVM);
 
@@ -1992,11 +2018,19 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
 		} else {
 			interfaceHead = markInterfaces(romClass, superclass, hostClassLoader, &foundCloneable, &interfaceCount, &inheritedInterfaceCount, &maxInterfaceDepth);
 			/* Compute the number of slots required for the interpreter and jit (if enabled) vTables. */
-			vTable = computeVTable(vmThread, classLoader, superclass, romClass, packageID, methodRemapArray, interfaceHead, &defaultConflictCount, interfaceCount, inheritedInterfaceCount);
+			vTable = computeVTable(vmThread, classLoader, superclass, romClass, packageID, methodRemapArray, interfaceHead, &defaultConflictCount, interfaceCount, inheritedInterfaceCount, &errorData);
 			if (vTable == NULL) {
-				classSize = 0;
 				unmarkInterfaces(interfaceHead);
-				goto computeDone;
+				popFromClassLoadingStack(vmThread);
+				omrthread_monitor_exit(javaVM->classTableMutex);
+				if (NULL != errorData.loader1) {
+					J9UTF8 *methodNameUTF = errorData.methodNameUTF;
+					J9UTF8 *methodSigUTF = errorData.methodSigUTF;
+					setClassLoadingConstraintOverrideError(vmThread, J9ROMCLASS_CLASSNAME(romClass), errorData.loader1, errorData.class1NameUTF, errorData.loader2, errorData.class2NameUTF, errorData.exceptionClassNameUTF, J9UTF8_DATA(methodNameUTF), J9UTF8_LENGTH(methodNameUTF), J9UTF8_DATA(methodSigUTF), J9UTF8_LENGTH(methodSigUTF));
+				} else {
+					setNativeOutOfMemoryError(vmThread, 0, 0);
+				}
+				return internalCreateRAMClassDoneNoMutex(vmThread, romClass, options, state);
 			}
 			vTableSlots = *vTable;
 			/* account for size slot */
@@ -2047,9 +2081,6 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
 		
 		/* Convert count to bytes and round to required alignment */
 		classSize *= sizeof(UDATA);
-		
-computeDone:
-		;
 	}
 	Trc_VM_CreateRAMClassFromROMClass_calculatedRAMSize(vmThread, classSize);