/******************************************************************************* * Copyright IBM Corp. and others 1991 * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ * or the Apache License, Version 2.0 which accompanies this distribution and * is available at https://www.apache.org/licenses/LICENSE-2.0. * * This Source Code may also be made available under the following * Secondary Licenses when the conditions for such availability set * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU * General Public License, version 2 with the GNU Classpath * Exception [1] and GNU General Public License, version 2 with the * OpenJDK Assembly Exception [2]. * * [1] https://www.gnu.org/software/classpath/license.html * [2] https://openjdk.org/legal/assembly-exception.html * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 *******************************************************************************/ #include "jvmtiHelpers.h" #include "jvmti_internal.h" typedef struct J9JVMTIClassStats { J9JavaVM * vm; J9VMThread * currentThread; jint classCount; jclass * classRefs; } J9JVMTIClassStats; #define ENSURE_CLASS_PRIMITIVE_ARRAY_OR_PREPARED(clazz) \ do { \ if (J9ROMCLASS_IS_PRIMITIVE_OR_ARRAY((clazz)->romClass) == 0) { \ if ((getClassStatus(clazz) & JVMTI_CLASS_STATUS_PREPARED) == 0) { \ JVMTI_ERROR(JVMTI_ERROR_CLASS_NOT_PREPARED); \ } \ } \ } while(0) /* Static J9UTF8's used to for MethodHandle.invokeExact/invoke methods */ #define DECLARE_CLASSNAME_LIST_DATA(instanceName, name) \ static const struct { \ U_16 length; \ U_8 data[sizeof(name) - 1]; \ } instanceName = { sizeof(name) - 1, name } DECLARE_CLASSNAME_LIST_DATA(MH_invokeExact_utf, "invokeExact"); DECLARE_CLASSNAME_LIST_DATA(MH_invoke_utf, "invoke"); DECLARE_CLASSNAME_LIST_DATA(MH_methodHandle_utf, "java.lang.invoke.MethodHandle"); static jvmtiError redefineClassesCommon(jvmtiEnv* env, jint class_count, const jvmtiClassDefinition* class_definitions, J9VMThread * currentThread, UDATA options); static void restoreBreakpointsInClasses (J9VMThread * currentThread, J9HashTable* classHashTable); static void clearBreakpointsInClasses (J9VMThread * currentThread, J9HashTable* classHashTable); static jint getClassStatus (J9Class * clazz); static UDATA countInitiatedClass (J9Class * clazz, J9JVMTIClassStats * results); static UDATA copyInitiatedClass (J9Class * clazz, J9JVMTIClassStats * results); static UDATA jvmtiGetConstantPool_HashFn(void *entry, void *userData); static UDATA jvmtiGetConstantPool_HashEqualFn(void *lhsEntry, void *rhsEntry, void *userData); static jvmtiError jvmtiGetConstantPool_writeConstants(jvmtiGcp_translation *translation, unsigned char *constantPoolBuf); static jvmtiError jvmtiGetConstantPool_addNAS(jvmtiGcp_translation *translation, J9ROMNameAndSignature *nas, U_32 *sunCpIndex, U_32 *refIndex); static jvmtiError jvmtiGetConstantPool_addReference(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMFieldRef *ref, U_32 *sunCpIndex); static jvmtiError jvmtiGetConstantPool_addUTF8(jvmtiGcp_translation *translation, J9UTF8 *utf8, U_32 *sunCpIndex, U_32 *refIndex); static jvmtiError jvmtiGetConstantPool_addClassOrString(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9UTF8 *utf8, U_32 *sunCpIndex, U_32 *refIndex); static jvmtiError jvmtiGetConstantPool_addIntFloat(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, U_32 data, U_32 *sunCpIndex); static jvmtiError jvmtiGetConstantPool_addLongDouble(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, U_32 slot1, U_32 slot2, U_32 *sunCpIndex); static jvmtiError jvmtiGetConstantPool_addMethodHandle(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMMethodHandleRef *ref, U_32 *sunCpIndex); static jvmtiError jvmtiGetConstantPool_addMethodType(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMMethodTypeRef *ref, U_32 *sunCpIndex); static jvmtiError jvmtiGetConstantPool_addNAS_name_sig(jvmtiGcp_translation *translation, void * key, J9UTF8 *name, J9UTF8* sig, U_32 *sunCpIndex, U_32 *refIndex); static jvmtiError jvmtiGetConstantPool_addInvokeDynamic(jvmtiGcp_translation *translation, UDATA key, J9ROMNameAndSignature *nameAndSig, U_32 bsmIndex, U_32 *sunCpIndex); /** * \brief Get the count of loaded classes * * @param[in] vm * @return Count of loaded classes * * Assumes caller acquired classTableMutex */ static jint jvmtiGetLoadedClassesCount(J9JavaVM * vm) { J9InternalVMFunctions const * const vmfuncs = vm->internalVMFunctions; J9ClassWalkState classWalkState; jint classCount = 0; J9Class *clazz = NULL; /* Count classes (ignore primitive types and old versions of redefined classes) */ clazz = vmfuncs->allLiveClassesStartDo(&classWalkState, vm, NULL); while (clazz) { if (J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass) == 0) { if ((J9CLASS_FLAGS(clazz) & J9AccClassHotSwappedOut) == 0) { classCount++; } } clazz = vmfuncs->allLiveClassesNextDo(&classWalkState); } vmfuncs->allLiveClassesEndDo(&classWalkState); return classCount; } jvmtiError JNICALL jvmtiGetLoadedClasses(jvmtiEnv* env, jint* class_count_ptr, jclass** classes_ptr) { J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_ENV(env); J9JavaVM * vm = JAVAVM_FROM_ENV(env); J9VMThread * currentThread; jclass * classRefs = NULL; jvmtiError rc; PORT_ACCESS_FROM_JAVAVM(vm); jclass *rv_classes = NULL; jint rv_class_count = 0; J9InternalVMFunctions const * const vmfuncs = vm->internalVMFunctions; Trc_JVMTI_jvmtiGetLoadedClasses_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9ClassWalkState classWalkState; jint lastClassCount; J9Class *clazz; vmfuncs->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_LIVE(env); ENSURE_NON_NULL(class_count_ptr); ENSURE_NON_NULL(classes_ptr); omrthread_monitor_enter(vm->classTableMutex); lastClassCount = (jint) jvmtiData->lastClassCount; if (lastClassCount == 0) { lastClassCount = jvmtiGetLoadedClassesCount(vm); } classRefs = j9mem_allocate_memory(lastClassCount * sizeof(jclass), J9MEM_CATEGORY_JVMTI_ALLOCATE); if (classRefs == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { jint i = 0; /* Copy class references (ignore primitive types and old versions of redefined classes) */ clazz = vmfuncs->allLiveClassesStartDo(&classWalkState, vm, NULL); while (clazz) { /* Check if we need more memory */ if (i == lastClassCount) { jclass * tempClassRefs; lastClassCount = lastClassCount + 128; tempClassRefs = j9mem_reallocate_memory(classRefs, lastClassCount * sizeof(jclass), J9MEM_CATEGORY_JVMTI); if (NULL == tempClassRefs) { /* realloc failed - need to free the original allocation */ j9mem_free_memory(classRefs); classRefs = NULL; vmfuncs->allLiveClassesEndDo(&classWalkState); omrthread_monitor_exit(vm->classTableMutex); rc = JVMTI_ERROR_OUT_OF_MEMORY; goto done; } classRefs = tempClassRefs; } if (J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass) == 0) { if ((J9CLASS_FLAGS(clazz) & J9AccClassHotSwappedOut) == 0) { classRefs[i++] = (jclass)vmfuncs->j9jni_createLocalRef((JNIEnv *) currentThread, J9VM_J9CLASS_TO_HEAPCLASS(clazz)); } } clazz = vmfuncs->allLiveClassesNextDo(&classWalkState); } vmfuncs->allLiveClassesEndDo(&classWalkState); jvmtiData->lastClassCount = (UDATA) i; rv_class_count = i; rv_classes = classRefs; } omrthread_monitor_exit(vm->classTableMutex); done: vmfuncs->internalExitVMToJNI(currentThread); } if (NULL != class_count_ptr) { *class_count_ptr = rv_class_count; } if (NULL != classes_ptr) { *classes_ptr = rv_classes; } TRACE_JVMTI_RETURN(jvmtiGetLoadedClasses); } jvmtiError JNICALL jvmtiGetClassLoaderClasses(jvmtiEnv* env, jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); J9VMThread * currentThread = NULL; jvmtiError rc = JVMTI_ERROR_NONE; jint rv_class_count = 0; jclass *rv_classes = NULL; Trc_JVMTI_jvmtiGetClassLoaderClasses_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions; J9ClassLoader *loader = NULL; J9Class *clazz = NULL; PORT_ACCESS_FROM_JAVAVM(vm); J9JVMTIClassStats stats = {0}; J9HashTableState hashWalkState = {0}; J9Class **primitiveArray = NULL; vmFuncs->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_LIVE(env); ENSURE_NON_NULL(class_count_ptr); ENSURE_NON_NULL(classes_ptr); /* NULL means to use the boot loader */ if (initiating_loader == NULL) { loader = vm->systemClassLoader; } else { loader = J9VMJAVALANGCLASSLOADER_VMREF(currentThread, J9_JNI_UNWRAP_REFERENCE(initiating_loader)); if (NULL == loader) { goto done; } } omrthread_monitor_enter(vm->classTableMutex); stats.vm = vm; stats.currentThread = currentThread; /* Search for classes who have this class loader as the initiating loader (wind up count) */ clazz = vmFuncs->hashClassTableStartDo(loader, &hashWalkState, J9_HASH_TABLE_STATE_FLAG_SKIP_HIDDEN); while (clazz != NULL) { countInitiatedClass(clazz, &stats); clazz = vmFuncs->hashClassTableNextDo(&hashWalkState); } /* The base type primitive array classes must also be added */ primitiveArray = &vm->booleanArrayClass; do { countInitiatedClass(*primitiveArray, &stats); primitiveArray += 1; } while (primitiveArray <= &vm->longArrayClass); stats.classRefs = j9mem_allocate_memory(stats.classCount * sizeof(jclass), J9MEM_CATEGORY_JVMTI_ALLOCATE); if (stats.classRefs == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { /* Save count before it gets modified below... */ rv_class_count = (jint) stats.classCount; rv_classes = stats.classRefs; /* Record classes who have this class loader as the initiating loader (wind down count) */ clazz = vmFuncs->hashClassTableStartDo(loader, &hashWalkState, J9_HASH_TABLE_STATE_FLAG_SKIP_HIDDEN); while (clazz != NULL) { copyInitiatedClass(clazz, &stats); clazz = vmFuncs->hashClassTableNextDo(&hashWalkState); } /* The base type primitive array classes must also be added */ primitiveArray = &vm->booleanArrayClass; do { copyInitiatedClass(*primitiveArray, &stats); primitiveArray += 1; } while (primitiveArray <= &vm->longArrayClass); } omrthread_monitor_exit(vm->classTableMutex); done: vmFuncs->internalExitVMToJNI(currentThread); } if (NULL != class_count_ptr) { *class_count_ptr = rv_class_count; } if (NULL != classes_ptr) { *classes_ptr = rv_classes; } TRACE_JVMTI_RETURN(jvmtiGetClassLoaderClasses); } #if JAVA_SPEC_VERSION >= 17 void fixHiddenOrAnonSignature(char* signature, UDATA signatureLength) { if (signatureLength >= (ROM_ADDRESS_LENGTH + 2)) { IDATA lastSeparatorIndex = signatureLength - (ROM_ADDRESS_LENGTH + 2); /* hidden/anon class names are generated from ROMClassBuilder::handleAnonClassName() and have 0x<romaddress> appended at the end */ if ((ANON_CLASSNAME_CHARACTER_SEPARATOR == signature[lastSeparatorIndex]) && ('0' == signature[lastSeparatorIndex+1]) && ('x' == signature[lastSeparatorIndex+2]) ) { signature[lastSeparatorIndex] = '.'; } } } #endif /* JAVA_SPEC_VERSION >= 17 */ jvmtiError JNICALL jvmtiGetClassSignature(jvmtiEnv* env, jclass klass, char** signature_ptr, char** generic_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; char * signature = NULL; char * generic = NULL; PORT_ACCESS_FROM_JAVAVM(vm); char *rv_signature = NULL; char *rv_generic = NULL; Trc_JVMTI_jvmtiGetClassSignature_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); /* Get signature if requested */ if (signature_ptr != NULL) { if (J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass)) { signature = j9mem_allocate_memory(2, J9MEM_CATEGORY_JVMTI_ALLOCATE); if (signature == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; goto done; } signature[0] = (clazz->arrayClass == NULL) ? 'V' : J9UTF8_DATA(J9ROMCLASS_CLASSNAME(clazz->arrayClass->romClass))[1]; signature[1] = '\0'; } else if (J9ROMCLASS_IS_ARRAY(clazz->romClass)) { J9ArrayClass * arrayClass = (J9ArrayClass *) clazz; J9Class * leafType = arrayClass->leafComponentType; UDATA arity = arrayClass->arity; UDATA allocSize = arity; J9UTF8 * utf; UDATA utfLength; if (J9ROMCLASS_IS_PRIMITIVE_TYPE(leafType->romClass)) { allocSize += 1; /* +1 or primitive sig char */ } else { utf = J9ROMCLASS_CLASSNAME(leafType->romClass); utfLength = J9UTF8_LENGTH(utf); allocSize += (utfLength + 2); /* +2 for L and ; */ } signature = j9mem_allocate_memory(allocSize + 1, J9MEM_CATEGORY_JVMTI_ALLOCATE); if (signature == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; goto done; } memset(signature, '[', arity); if (J9ROMCLASS_IS_PRIMITIVE_TYPE(leafType->romClass)) { signature[arrayClass->arity] = J9UTF8_DATA(J9ROMCLASS_CLASSNAME(leafType->arrayClass->romClass))[1]; } else { J9UTF8 * utf = J9ROMCLASS_CLASSNAME(leafType->romClass); UDATA utfLength = J9UTF8_LENGTH(utf); signature[arity] = 'L'; memcpy(signature + arity + 1, J9UTF8_DATA(utf), utfLength); signature[allocSize - 1] = ';'; #if JAVA_SPEC_VERSION >= 17 if (J9ROMCLASS_IS_ANON_OR_HIDDEN(leafType->romClass)) { fixHiddenOrAnonSignature(signature, allocSize); } #endif /* JAVA_SPEC_VERSION >= 17 */ } signature[allocSize] = '\0'; } else { J9UTF8 * utf = J9ROMCLASS_CLASSNAME(clazz->romClass); UDATA utfLength = J9UTF8_LENGTH(utf); signature = j9mem_allocate_memory(utfLength + 3, J9MEM_CATEGORY_JVMTI_ALLOCATE); if (signature == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; goto done; } signature[0] = 'L'; memcpy(signature + 1, J9UTF8_DATA(utf), utfLength); signature[utfLength + 1] = ';'; signature[utfLength + 2] = '\0'; #if JAVA_SPEC_VERSION >= 17 if (J9ROMCLASS_IS_ANON_OR_HIDDEN(clazz->romClass)) { fixHiddenOrAnonSignature(signature, utfLength + 2); } #endif /* JAVA_SPEC_VERSION >= 17 */ } } /* Get generic signature if requested */ if (generic_ptr != NULL) { J9UTF8 * genericSignature = getGenericSignatureForROMClass(vm, clazz->classLoader, clazz->romClass); if (genericSignature != NULL) { rc = cStringFromUTF(env, genericSignature, &generic); releaseOptInfoBuffer(vm, clazz->romClass); if (rc != JVMTI_ERROR_NONE) { goto done; } } } rv_signature = signature; rv_generic = generic; done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (rc != JVMTI_ERROR_NONE) { j9mem_free_memory(signature); j9mem_free_memory(generic); } if (NULL != signature_ptr) { *signature_ptr = rv_signature; } if (NULL != generic_ptr) { *generic_ptr = rv_generic; } TRACE_JVMTI_RETURN(jvmtiGetClassSignature); } jvmtiError JNICALL jvmtiGetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jint rv_status = JVMTI_CLASS_STATUS_ERROR; Trc_JVMTI_jvmtiGetClassStatus_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class* clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(status_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); rv_status = getClassStatus(clazz); done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != status_ptr) { *status_ptr = rv_status; } TRACE_JVMTI_RETURN(jvmtiGetClassStatus); } jvmtiError JNICALL jvmtiGetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; char *rv_source_name = NULL; Trc_JVMTI_jvmtiGetSourceFileName_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9UTF8 *sourceFileName; J9Class* clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_CAPABILITY(env, can_get_source_file_name); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(source_name_ptr); /* Assume that having the capability means that the debug info server is active */ clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); rc = JVMTI_ERROR_ABSENT_INFORMATION; sourceFileName = getSourceFileNameForROMClass(vm, clazz->classLoader, clazz->romClass); if (sourceFileName != NULL) { rc = cStringFromUTF(env, sourceFileName, &rv_source_name); releaseOptInfoBuffer(vm, clazz->romClass); } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != source_name_ptr) { *source_name_ptr = rv_source_name; } TRACE_JVMTI_RETURN(jvmtiGetSourceFileName); } jvmtiError JNICALL jvmtiGetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jint rv_modifiers = 0; Trc_JVMTI_jvmtiGetClassModifiers_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; J9ROMClass * romClass; U_32 modifiers; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(modifiers_ptr); /* Primitive classes have the correct modifiers, so no special processing needs to be done */ clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); romClass = clazz->romClass; /* Array classes have the modifiers of their leaf component type */ if (J9ROMCLASS_IS_ARRAY(romClass)) { clazz = ((J9ArrayClass *) clazz)->leafComponentType; } /* Inner classes use the memberAccessFlags instead of the modifiers */ modifiers = J9_ARE_NO_BITS_SET(clazz->romClass->extraModifiers, J9AccClassInnerClass) ? clazz->romClass->modifiers : clazz->romClass->memberAccessFlags; /* Array classes must always be final and not interface */ if (J9ROMCLASS_IS_ARRAY(romClass)) { modifiers = (modifiers | J9AccFinal) & ~J9AccInterface; } /* Only the low 16 bit of the modifiers are specified - the rest are J9 internal */ rv_modifiers = (jint) (modifiers & 0xFFFF); done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != modifiers_ptr) { *modifiers_ptr = rv_modifiers; } TRACE_JVMTI_RETURN(jvmtiGetClassModifiers); } jvmtiError JNICALL jvmtiGetClassMethods(jvmtiEnv* env, jclass klass, jint* method_count_ptr, jmethodID** methods_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; PORT_ACCESS_FROM_JAVAVM(vm); jint rv_method_count = 0; jmethodID *rv_methods = NULL; Trc_JVMTI_jvmtiGetClassMethods_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; UDATA methodCount; jmethodID * methodIDs; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(method_count_ptr); ENSURE_NON_NULL(methods_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); ENSURE_CLASS_PRIMITIVE_ARRAY_OR_PREPARED(clazz); methodCount = clazz->romClass->romMethodCount; methodIDs = j9mem_allocate_memory(methodCount * sizeof(jmethodID), J9MEM_CATEGORY_JVMTI_ALLOCATE); if (methodIDs == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { J9Method * methods = clazz->ramMethods; UDATA i; for (i = 0; i < methodCount; ++i) { J9Method * method; J9JNIMethodID * methodID; method = &(methods[i]); methodID = vm->internalVMFunctions->getJNIMethodID(currentThread, method); if (methodID == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; j9mem_free_memory(methodIDs); goto done; } methodIDs[i] = (jmethodID) methodID; } rv_method_count = (jint) methodCount; rv_methods = methodIDs; } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != method_count_ptr) { *method_count_ptr = rv_method_count; } if (NULL != methods_ptr) { *methods_ptr = rv_methods; } TRACE_JVMTI_RETURN(jvmtiGetClassMethods); } jvmtiError JNICALL jvmtiGetClassFields(jvmtiEnv* env, jclass klass, jint* field_count_ptr, jfieldID** fields_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; PORT_ACCESS_FROM_JAVAVM(vm); jint rv_field_count = 0; jfieldID *rv_fields = NULL; Trc_JVMTI_jvmtiGetClassFields_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { const J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions; J9Class * clazz; J9ROMClass * romClass; UDATA fieldCount; jfieldID * fieldIDs; vmFuncs->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(field_count_ptr); ENSURE_NON_NULL(fields_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); ENSURE_CLASS_PRIMITIVE_ARRAY_OR_PREPARED(clazz); romClass = clazz->romClass; fieldCount = romClass->romFieldCount; fieldIDs = j9mem_allocate_memory(fieldCount * sizeof(jfieldID), J9MEM_CATEGORY_JVMTI_ALLOCATE); if (fieldIDs == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { J9ROMFieldOffsetWalkState state; J9ROMFieldOffsetWalkResult * result; UDATA i; i = 0; #if defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) result = vmFuncs->fieldOffsetsStartDo(vm, romClass, GET_SUPERCLASS(clazz), &state, J9VM_FIELD_OFFSET_WALK_INCLUDE_STATIC | J9VM_FIELD_OFFSET_WALK_INCLUDE_INSTANCE, clazz->flattenedClassCache); #else /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ result = vmFuncs->fieldOffsetsStartDo(vm, romClass, GET_SUPERCLASS(clazz), &state, J9VM_FIELD_OFFSET_WALK_INCLUDE_STATIC | J9VM_FIELD_OFFSET_WALK_INCLUDE_INSTANCE ); #endif /* defined(J9VM_OPT_VALHALLA_FLATTENABLE_VALUE_TYPES) */ while (NULL != result->field) { UDATA inconsistentData = 0; J9JNIFieldID * fieldID = vmFuncs->getJNIFieldID(currentThread, clazz, result->field, result->offset, &inconsistentData); /* vmaccess isn't given up so the J9ROMFieldShape and clazz->romClass must be consistent here */ Assert_JVMTI_true(0 == inconsistentData); if (fieldID == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; j9mem_free_memory(fieldIDs); goto done; } fieldIDs[i++] = (jfieldID) fieldID; result = vmFuncs->fieldOffsetsNextDo(&state); } rv_field_count = (jint) fieldCount; rv_fields = fieldIDs; } done: vmFuncs->internalExitVMToJNI(currentThread); } *field_count_ptr = rv_field_count; *fields_ptr = rv_fields; TRACE_JVMTI_RETURN(jvmtiGetClassFields); } jvmtiError JNICALL jvmtiGetImplementedInterfaces(jvmtiEnv* env, jclass klass, jint* interface_count_ptr, jclass** interfaces_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; PORT_ACCESS_FROM_JAVAVM(vm); jint rv_interface_count = 0; jclass *rv_interfaces = NULL; Trc_JVMTI_jvmtiGetImplementedInterfaces_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; J9ROMClass * romClass; jint interfaceCount = 0; jclass * interfaces = NULL; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(interface_count_ptr); ENSURE_NON_NULL(interfaces_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); ENSURE_CLASS_PRIMITIVE_ARRAY_OR_PREPARED(clazz); romClass = clazz->romClass; /* Array and primitive classes must return an empty list */ if (!J9ROMCLASS_IS_PRIMITIVE_OR_ARRAY(romClass)) { /* Must walk the interface names in the ROM class to be sure to get the whole list */ interfaceCount = (jint) romClass->interfaceCount; interfaces = j9mem_allocate_memory(interfaceCount * sizeof(jclass), J9MEM_CATEGORY_JVMTI_ALLOCATE); if (interfaces == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { J9SRP * interfaceNames = J9ROMCLASS_INTERFACES(romClass); jint i; for (i = 0; i < interfaceCount; i++) { J9Class * interfaceClass; J9UTF8 * interfaceName = SRP_PTR_GET(&interfaceNames[i], J9UTF8 *); /* The interfaces for this class are guaranteed to already be loaded */ interfaceClass = vm->internalVMFunctions->internalFindClassUTF8(currentThread, J9UTF8_DATA(interfaceName), J9UTF8_LENGTH(interfaceName), clazz->classLoader, J9_FINDCLASS_FLAG_EXISTING_ONLY); interfaces[i] = (jclass) vm->internalVMFunctions->j9jni_createLocalRef((JNIEnv *) currentThread, J9VM_J9CLASS_TO_HEAPCLASS(interfaceClass)); } } } rv_interface_count = interfaceCount; rv_interfaces = interfaces; done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != interface_count_ptr) { *interface_count_ptr = rv_interface_count; } if (NULL != interfaces_ptr) { *interfaces_ptr = rv_interfaces; } TRACE_JVMTI_RETURN(jvmtiGetImplementedInterfaces); } jvmtiError JNICALL jvmtiIsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jboolean rv_is_interface = JNI_FALSE; Trc_JVMTI_jvmtiIsInterface_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(is_interface_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); rv_is_interface = (clazz->romClass->modifiers & J9AccInterface) ? JNI_TRUE : JNI_FALSE; done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != is_interface_ptr) { *is_interface_ptr = rv_is_interface; } TRACE_JVMTI_RETURN(jvmtiIsInterface); } jvmtiError JNICALL jvmtiIsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jboolean rv_is_array_class = JNI_FALSE; Trc_JVMTI_jvmtiIsArrayClass_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(is_array_class_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); rv_is_array_class = (J9ROMCLASS_IS_ARRAY(clazz->romClass)) ? JNI_TRUE : JNI_FALSE; done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != is_array_class_ptr) { *is_array_class_ptr = rv_is_array_class; } TRACE_JVMTI_RETURN(jvmtiIsArrayClass); } jvmtiError JNICALL jvmtiGetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jobject rv_classloader = NULL; Trc_JVMTI_jvmtiGetClassLoader_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; J9ClassLoader * classLoader; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(classloader_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); classLoader = clazz->classLoader; if (classLoader == vm->systemClassLoader) { rv_classloader = NULL; } else { rv_classloader = vm->internalVMFunctions->j9jni_createLocalRef((JNIEnv *)currentThread, J9CLASSLOADER_CLASSLOADEROBJECT(currentThread, classLoader)); } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != classloader_ptr) { *classloader_ptr = rv_classloader; } TRACE_JVMTI_RETURN(jvmtiGetClassLoader); } jvmtiError JNICALL jvmtiGetSourceDebugExtension(jvmtiEnv* env, jclass klass, char** source_debug_extension_ptr) { jvmtiError rc; #if defined(J9VM_OPT_DEBUG_JSR45_SUPPORT) J9JavaVM * vm = JAVAVM_FROM_ENV(env); J9VMThread * currentThread; char *rv_source_debug_extension = NULL; Trc_JVMTI_jvmtiGetSourceDebugExtension_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9SourceDebugExtension * sourceDebugExtension; J9Class* clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_CAPABILITY(env, can_get_source_debug_extension); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(source_debug_extension_ptr); /* Assume that having the capability means that the debug info server is active */ rc = JVMTI_ERROR_ABSENT_INFORMATION; clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); sourceDebugExtension = getSourceDebugExtensionForROMClass(vm, clazz->classLoader, clazz->romClass); if (sourceDebugExtension != NULL) { U_32 length = sourceDebugExtension->size; if (length != 0) { rc = cStringFromUTFChars(env, (U_8 *) (sourceDebugExtension + 1), length, &rv_source_debug_extension); } releaseOptInfoBuffer(vm, clazz->romClass); } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } #else Trc_JVMTI_jvmtiGetSourceDebugExtension_Entry(env); rc = JVMTI_ERROR_MUST_POSSESS_CAPABILITY; #endif if (NULL != source_debug_extension_ptr) { *source_debug_extension_ptr = rv_source_debug_extension; } TRACE_JVMTI_RETURN(jvmtiGetSourceDebugExtension); } jvmtiError JNICALL jvmtiRedefineClasses(jvmtiEnv* env, jint class_count, const jvmtiClassDefinition* class_definitions) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm); Trc_JVMTI_jvmtiRedefineClasses_Entry(env); omrthread_monitor_enter(jvmtiData->redefineMutex); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_LIVE(env); ENSURE_CAPABILITY(env, can_redefine_classes); ENSURE_NON_NEGATIVE(class_count); ENSURE_NON_NULL(class_definitions); rc = redefineClassesCommon(env, class_count, class_definitions, currentThread, J9_FINDCLASS_FLAG_REDEFINING); done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } omrthread_monitor_exit(jvmtiData->redefineMutex); TRACE_JVMTI_RETURN(jvmtiRedefineClasses); } static void fixWatchedFields(J9JavaVM *vm, J9HashTable *classHashTable) { if (NULL != classHashTable) { J9JVMTIData *jvmtiData = vm->jvmtiData; pool_state envPoolState; J9JVMTIEnv *j9env = pool_startDo(jvmtiData->environments, &envPoolState); while (j9env != NULL) { /* CMVC 196966: only inspect live (not disposed) environments */ if (0 == (j9env->flags & J9JVMTIENV_FLAG_DISPOSED)) { J9HashTableState walkState; J9JVMTIWatchedClass *watchedClass = hashTableStartDo(j9env->watchedClasses, &walkState); while (NULL != watchedClass) { J9JVMTIClassPair exemplar; J9JVMTIClassPair *result = NULL; exemplar.originalRAMClass = watchedClass->clazz; result = hashTableFind(classHashTable, &exemplar); if (NULL != result) { J9Class *replacementRAMClass = result->replacementClass.ramClass; if (NULL != replacementRAMClass) { watchedClass->clazz = replacementRAMClass; } } watchedClass = hashTableNextDo(&walkState); } } j9env = pool_nextDo(&envPoolState); } } } /** * Update the method ordering table for an interface class. The cases are: * * 1) A reodering is required and none exists yet * Assign the remap array to the interface class * 2) A reordring is not required, and the class already has one * Free and NULL the interface class ordering array * 3) A reodering is required and the class already has one * Copy the method remap over the existing ordering in the class * 4) No reordering required and the class does not have one * Do nothing * * @param[in] currentThread the current J9VMThread * @param[in] count The number of explicitly-redefined classes * @param[in] classPair Pointer to the array of class pairs (of size count) */ static void updateInterfaceMethodOrdering(J9VMThread *currentThread, jint count, J9JVMTIClassPair *classPair) { while (0 != count) { J9Class *interfaceClass = classPair->originalRAMClass; J9ROMClass *romClass = interfaceClass->romClass; if (J9ROMCLASS_IS_INTERFACE(romClass)) { U_32 *ordering = J9INTERFACECLASS_METHODORDERING(interfaceClass); U_32 *remap = classPair->methodRemapIndices; if (NULL == ordering) { /* No reordering exists yet */ if (NULL != remap) { /* First reordering required - assign the remap array and NULL it * in the pair to prevent it being freed. */ J9INTERFACECLASS_SET_METHODORDERING(interfaceClass, remap); classPair->methodRemapIndices = NULL; } } else { /* Ordering already exists */ if (NULL == remap) { /* Back to the original ordering, discard the ordering array */ PORT_ACCESS_FROM_VMC(currentThread); J9INTERFACECLASS_SET_METHODORDERING(interfaceClass, NULL); j9mem_free_memory(ordering); } else { /* Reordering still required - copy the remap array over the existing ordering */ memcpy(ordering, remap, romClass->romMethodCount * sizeof(U_32)); } } } classPair += 1; count -= 1; } } static jvmtiError redefineClassesCommon(jvmtiEnv* env, jint class_count, const jvmtiClassDefinition* class_definitions, J9VMThread * currentThread, UDATA options) { jvmtiError rc; J9JavaVM * vm = currentThread->javaVM; PORT_ACCESS_FROM_JAVAVM(vm); UDATA extensionsEnabled = 0; UDATA extensionsUsed = 0; /** Set to true when redefine extensions have been used */ J9JVMTIClassPair * specifiedClasses = NULL; J9HashTable * methodPairs = NULL; J9HashTable * classPairs = NULL; J9HashTable * methodEquivalences = NULL; #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) j9object_t memberNamesToFix = NULL; #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ #ifdef J9VM_INTERP_NATIVE_SUPPORT J9JVMTIHCRJitEventData jitEventData; #endif J9JVMTIHCRJitEventData *jitEventDataPtr = NULL; UDATA safePoint = J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags, J9_EXTENDED_RUNTIME_OSR_SAFE_POINT); #ifdef J9VM_INTERP_NATIVE_SUPPORT /* Ensure that jitEventData is initialized in case we hit failure handling before * it is fully initialized */ memset(&jitEventData, 0, sizeof(J9JVMTIHCRJitEventData)); #endif /* Check whether we should permit j9 specific class redefine extensions */ extensionsEnabled = areExtensionsEnabled(vm); /* Verify that all of the classes are allowed to be replaced */ rc = verifyClassesCanBeReplaced(currentThread, class_count, class_definitions); if (rc != JVMTI_ERROR_NONE) { goto failed; } /* Allocate a buffer to hold the new versions of the classes */ specifiedClasses = j9mem_allocate_memory(class_count * sizeof(J9JVMTIClassPair), J9MEM_CATEGORY_JVMTI); if (specifiedClasses == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; goto failed; } memset(specifiedClasses, 0, class_count * sizeof(J9JVMTIClassPair)); /* Create ROM classes for each of the replaced classes */ rc = reloadROMClasses(currentThread, class_count, class_definitions, specifiedClasses, options); if (rc != JVMTI_ERROR_NONE) { goto failed; } /* Make sure the replaced classes are compatible with the old versions */ rc = verifyClassesAreCompatible(currentThread, class_count, specifiedClasses, extensionsEnabled, &extensionsUsed); if (rc != JVMTI_ERROR_NONE) { goto failed; } if (NULL != vm->bytecodeVerificationData) { rc = verifyNewClasses(currentThread, class_count, specifiedClasses); } if (rc != JVMTI_ERROR_NONE) { goto failed; } #ifdef J9VM_INTERP_NATIVE_SUPPORT if (!extensionsUsed && vm->jitConfig) { jitEventDataPtr = &jitEventData; } #endif if (safePoint) { vm->internalVMFunctions->acquireSafePointVMAccess(currentThread); } else { vm->internalVMFunctions->acquireExclusiveVMAccess(currentThread); } #if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) omrthread_rwmutex_enter_write(vm->classUnloadMutex); #else /* defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) */ omrthread_monitor_enter(vm->classUnloadMutex); #endif /* defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) */ /* All compilations are paused waiting for either the class unload mutex or VM access. * We can update the VM data structures without any inconsistencies being observed by * concurrently running compilations. Afterward, resuming compilations will be interrupted * if necessary before they can observe an inconsistency. * * NOTE: The logic below can cause GC to run, and GC could decide to unload classes. * To avoid deadlock, prevent GC from redundantly entering the class unload mutex. */ vm->isClassUnloadMutexHeldForRedefinition = TRUE; if (J9_ARE_ANY_BITS_SET(vm->runtimeFlags, J9_RUNTIME_DYNAMIC_HEAPIFICATION)) { /* Look for stack-allocated objects only if the JIT is enabled and not running in FSD mode */ if ((NULL != vm->jitConfig) && !J9_FSD_ENABLED(vm)) { rc = heapifyStackAllocatedObjects(currentThread, safePoint); if (JVMTI_ERROR_NONE != rc) { goto failedWithVMAccess; } } } /* Determine all ROM classes which need a new RAM class, and pair them with their current RAM class. */ rc = determineClassesToRecreate(currentThread, class_count, specifiedClasses, &classPairs, &methodPairs, jitEventDataPtr, !extensionsEnabled); if (rc == JVMTI_ERROR_NONE) { #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Identify the MemberNames needing fix-up based on classPairs. */ memberNamesToFix = prepareToFixMemberNames(currentThread, classPairs); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ /* Recreate the RAM classes for all classes */ rc = recreateRAMClasses(currentThread, classPairs, methodPairs, extensionsUsed, !extensionsEnabled); if (rc != JVMTI_ERROR_NONE) { goto failedWithVMAccess; } if (!extensionsEnabled) { /* Fast HCR path - where the J9Class is redefined in place. */ /* Add method equivalences for the methods that were re-defined (reverse of before!). Propagate any equivalent resolved callsites. */ rc = fixMethodEquivalencesAndCallSites(currentThread, classPairs, jitEventDataPtr, TRUE, &methodEquivalences, extensionsUsed); if (rc != JVMTI_ERROR_NONE) { goto failedWithVMAccess; } /* Fix the vTables of all subclasses */ fixVTables_forNormalRedefine(currentThread, classPairs, methodPairs, TRUE, &methodEquivalences); #if defined(J9VM_OPT_METHOD_HANDLE) /* Update method references in DirectHandles */ fixDirectHandles(currentThread, classPairs, methodPairs); #endif /* defined(J9VM_OPT_METHOD_HANDLE) */ /* Fix JNI */ fixJNIRefs(currentThread, classPairs, TRUE, extensionsUsed); #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Fix MemberNames (vmtarget) */ fixMemberNames(currentThread, &memberNamesToFix); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ /* Fix resolved constant pool references to point to new methods. */ fixConstantPoolsForFastHCR(currentThread, classPairs, methodPairs); /* Flush the reflect method cache */ flushClassLoaderReflectCache(currentThread, classPairs); /* Store the method remap indices for redefined interface classes */ updateInterfaceMethodOrdering(currentThread, class_count, specifiedClasses); /* Indicate that a redefine has occurred */ vm->hotSwapCount += 1; /* Notify the JIT about redefined classes */ jitClassRedefineEvent(currentThread, &jitEventData, FALSE, FALSE); /* Clear/suspend all breakpoints in the classes being replaced */ clearBreakpointsInClasses(currentThread, classPairs); } else { /* Clear/suspend all breakpoints in the classes being replaced */ clearBreakpointsInClasses(currentThread, classPairs); /* Fix static refs */ fixStaticRefs(currentThread, classPairs, extensionsUsed); /* Update heap references */ fixHeapRefs(vm, classPairs); #if defined(J9VM_OPT_METHOD_HANDLE) /* Update method references in DirectHandles */ fixDirectHandles(currentThread, classPairs, methodPairs); #endif /* defined(J9VM_OPT_METHOD_HANDLE) */ /* Copy preserved values */ copyPreservedValues(currentThread, classPairs, extensionsUsed); /* Update the componentType and leafComponentType fields of array classes */ fixArrayClasses(currentThread, classPairs); /* Fix JNI */ fixJNIRefs(currentThread, classPairs, FALSE, extensionsUsed); /* Update the iTables of any classes which implement a replaced interface */ fixITables(currentThread, classPairs); /* Fix subclass hierarchy */ fixSubclassHierarchy(currentThread, classPairs); /* Unresolve all classes */ unresolveAllClasses(currentThread, classPairs, methodPairs, extensionsUsed); /* Update method equivalences. Propagate any equivalent resolved callsites. */ rc = fixMethodEquivalencesAndCallSites(currentThread, classPairs, jitEventDataPtr, FALSE, &methodEquivalences, extensionsUsed); if (rc != JVMTI_ERROR_NONE) { goto failedWithVMAccess; } /* Fix the vTables of all subclasses */ if (!extensionsUsed) { fixVTables_forNormalRedefine(currentThread, classPairs, methodPairs, FALSE, &methodEquivalences); } #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Fix MemberNames (vmtarget) */ fixMemberNames(currentThread, &memberNamesToFix); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ /* Restore breakpoints in the implicitly replaced classes */ restoreBreakpointsInClasses(currentThread, classPairs); /* Flush the reflect method cache */ flushClassLoaderReflectCache(currentThread, classPairs); /* Fix watched fields */ fixWatchedFields(vm, classPairs); /* Indicate that a redefine has occurred */ vm->hotSwapCount += 1; #if JAVA_SPEC_VERSION >= 11 /* Update nests with redefined nest tops */ fixNestMembers(currentThread, classPairs); #endif /* JAVA_SPEC_VERSION >= 11 */ #ifdef J9VM_INTERP_NATIVE_SUPPORT /* Notify the JIT about redefined classes */ jitClassRedefineEvent(currentThread, &jitEventData, extensionsEnabled, extensionsUsed); #endif } notifyGCOfClassReplacement(currentThread, classPairs, !extensionsEnabled); } J9JVMTI_DATA_FROM_VM(vm)->flags = J9JVMTI_DATA_FROM_VM(vm)->flags & ~J9JVMTI_FLAG_REDEFINE_CLASS_EXTENSIONS_USED; if (rc == JVMTI_ERROR_NONE) { TRIGGER_J9HOOK_VM_CLASSES_REDEFINED(vm->hookInterface, currentThread); } failedWithVMAccess: #if defined(J9VM_OPT_OPENJDK_METHODHANDLE) /* Once the MemberNames have been prepared, they need to be fixed even on error. */ fixMemberNames(currentThread, &memberNamesToFix); #endif /* defined(J9VM_OPT_OPENJDK_METHODHANDLE) */ hashTableFree(classPairs); vm->isClassUnloadMutexHeldForRedefinition = FALSE; #if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) omrthread_rwmutex_exit_write(vm->classUnloadMutex); #else /* defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) */ omrthread_monitor_exit(vm->classUnloadMutex); #endif /* defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR) */ if (safePoint) { vm->internalVMFunctions->releaseSafePointVMAccess(currentThread); } else { vm->internalVMFunctions->releaseExclusiveVMAccess(currentThread); } failed: if (specifiedClasses) { int i; J9JVMTIClassPair * classPair = specifiedClasses; for (i = 0; i < class_count; i++) { if (classPair->methodRemap) { j9mem_free_memory(classPair->methodRemap); } if (classPair->methodRemapIndices) { j9mem_free_memory(classPair->methodRemapIndices); } classPair++; } j9mem_free_memory(specifiedClasses); } hashTableFree(methodPairs); hashTableFree(methodEquivalences); #ifdef J9VM_INTERP_NATIVE_SUPPORT if (jitEventData.initialized) { jitEventFree(vm, &jitEventData); } #endif /* Clear any pending exception - any error is represented solely by the return code */ currentThread->currentException = NULL; currentThread->privateFlags &= ~(UDATA)J9_PRIVATE_FLAGS_REPORT_EXCEPTION_THROW; return rc; } static void clearBreakpointsInClasses(J9VMThread * currentThread, J9HashTable* classHashTable) { J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(currentThread->javaVM); J9HashTableState hashTableState; J9JVMTIClassPair * classPair; classPair = hashTableStartDo(classHashTable, &hashTableState); while (classPair != NULL) { J9Class * originalRAMClass = classPair->originalRAMClass; J9Class * replacementRAMClass = classPair->replacementClass.ramClass; J9JVMTIAgentBreakpointDoState state; J9JVMTIAgentBreakpoint * agentBreakpoint; UDATA explicitlyRecreated; if (replacementRAMClass == NULL) { classPair = hashTableNextDo(&hashTableState); continue; } explicitlyRecreated = (originalRAMClass->romClass != replacementRAMClass->romClass); agentBreakpoint = allAgentBreakpointsStartDo(jvmtiData, &state); while (agentBreakpoint != NULL) { if (J9_CLASS_FROM_METHOD(((J9JNIMethodID *) agentBreakpoint->method)->method) == originalRAMClass) { if (explicitlyRecreated) { deleteAgentBreakpoint(currentThread, state.j9env, agentBreakpoint); } else { suspendAgentBreakpoint(currentThread, agentBreakpoint); } } agentBreakpoint = allAgentBreakpointsNextDo(&state); } classPair = hashTableNextDo(&hashTableState); } } static UDATA countInitiatedClass(J9Class * clazz, J9JVMTIClassStats * results) { /* Count classes (ignore primitive types and old versions of redefined classes) */ if (((J9CLASS_FLAGS(clazz) & J9AccClassHotSwappedOut) == 0) && ((J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass)) == 0)) { do { results->classCount += 1; clazz = clazz->arrayClass; } while (NULL != clazz); } return 0; } static UDATA copyInitiatedClass(J9Class * clazz, J9JVMTIClassStats * results) { /* Copy classes (ignore primitive types and old versions of redefined classes) */ if (((J9CLASS_FLAGS(clazz) & J9AccClassHotSwappedOut) == 0) && (J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass) == 0)) { J9JavaVM * vm = results->vm; J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions; JNIEnv * jniEnv = (JNIEnv *)results->currentThread; do { jint slot = (jint)results->classCount - 1; /* Reverse fill */ if (slot >= 0) { j9object_t classObject = J9VM_J9CLASS_TO_HEAPCLASS(clazz); results->classRefs[slot] = (jclass)vmFuncs->j9jni_createLocalRef(jniEnv, classObject); results->classCount = slot; } clazz = clazz->arrayClass; } while (NULL != clazz); } return 0; } static void restoreBreakpointsInClasses(J9VMThread * currentThread, J9HashTable * classPairs) { J9HashTableState hashTableState; J9JVMTIClassPair * classPair; J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(currentThread->javaVM); classPair = hashTableStartDo(classPairs, &hashTableState); while (classPair != NULL) { J9Class * originalRAMClass = classPair->originalRAMClass; J9Class * replacementRAMClass = classPair->replacementClass.ramClass; /* Breakpoints in the explicitly recreated classes are gone - only restore those in implicitly recreated classes */ if (replacementRAMClass && (originalRAMClass->romClass == replacementRAMClass->romClass)) { J9JVMTIAgentBreakpointDoState state; J9JVMTIAgentBreakpoint * agentBreakpoint; agentBreakpoint = allAgentBreakpointsStartDo(jvmtiData, &state); while (agentBreakpoint != NULL) { /* JNI refs have been updated to point to the replacement classes by now */ if (J9_CLASS_FROM_METHOD(((J9JNIMethodID *) agentBreakpoint->method)->method) == replacementRAMClass) { installAgentBreakpoint(currentThread, agentBreakpoint); } agentBreakpoint = allAgentBreakpointsNextDo(&state); } } classPair = hashTableNextDo(&hashTableState); } } static jint getClassStatus(J9Class * clazz) { jint status = JVMTI_CLASS_STATUS_ERROR; if (J9ROMCLASS_IS_PRIMITIVE_TYPE(clazz->romClass)) { status = JVMTI_CLASS_STATUS_PRIMITIVE; } else if (J9ROMCLASS_IS_ARRAY(clazz->romClass)) { status = JVMTI_CLASS_STATUS_ARRAY; } else { switch (clazz->initializeStatus & J9ClassInitStatusMask) { case J9ClassInitUnverified: status = 0; break; case J9ClassInitUnprepared: status = JVMTI_CLASS_STATUS_VERIFIED; break; case J9ClassInitNotInitialized: status = JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED; break; case J9ClassInitSucceeded: status = JVMTI_CLASS_STATUS_INITIALIZED | JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED; break; case J9ClassInitFailed: status = JVMTI_CLASS_STATUS_ERROR | JVMTI_CLASS_STATUS_VERIFIED | JVMTI_CLASS_STATUS_PREPARED; break; } } return status; } jvmtiError JNICALL jvmtiRetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm); Trc_JVMTI_jvmtiRetransformClasses_Entry(env); omrthread_monitor_enter(jvmtiData->redefineMutex); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { jvmtiClassDefinition * class_definitions; PORT_ACCESS_FROM_JAVAVM(vm); vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_LIVE(env); ENSURE_CAPABILITY(env, can_retransform_classes); ENSURE_NON_NEGATIVE(class_count); ENSURE_NON_NULL(classes); /* Create a jvmtiClassDefinition for each retransformed class from the stored bytes and call the common redefine helper */ class_definitions = j9mem_allocate_memory(class_count * sizeof(jvmtiClassDefinition), J9MEM_CATEGORY_JVMTI); if (class_definitions == NULL) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } else { J9MemorySegmentList * classSegments = vm->classMemorySegments; jint i; memset(class_definitions, 0, class_count * sizeof(jvmtiClassDefinition)); omrthread_monitor_enter(classSegments->segmentMutex); for (i = 0; i < class_count; ++i) { jclass klass; J9Class * clazz; U_8 * classFileBytes = NULL; U_32 classFileBytesCount = 0; klass = classes[i]; if (klass == NULL) { rc = JVMTI_ERROR_INVALID_CLASS; break; } clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); if (!classIsModifiable(vm, clazz)) { rc = JVMTI_ERROR_UNMODIFIABLE_CLASS; break; } if (WSRP_GET(clazz->romClass->intermediateClassData, U_8*) == NULL) { rc = JVMTI_ERROR_UNMODIFIABLE_CLASS; break; } if (!J9ROMCLASS_IS_INTERMEDIATE_DATA_A_CLASSFILE(clazz->romClass)) { J9ROMClass * intermediateROMClass = (J9ROMClass *) J9ROMCLASS_INTERMEDIATECLASSDATA(clazz->romClass); IDATA result = 0; /* -XX:-StoreIntermediateClassfile (enabled by default) : recreate class file bytes from intermediateROMClass pointed by current ROMClass */ result = vm->dynamicLoadBuffers->transformROMClassFunction(vm, PORTLIB, intermediateROMClass, &classFileBytes, &classFileBytesCount); if (BCT_ERR_NO_ERROR != result) { Trc_JVMTI_jvmtiRetransformClasses_ErrorInRecreatingClassfile(env, intermediateROMClass, result); rc = JVMTI_ERROR_INTERNAL; if (BCT_ERR_OUT_OF_MEMORY == result) { rc = JVMTI_ERROR_OUT_OF_MEMORY; } break; } } else { classFileBytesCount = (jint) clazz->romClass->intermediateClassDataLength; classFileBytes = WSRP_GET(clazz->romClass->intermediateClassData, U_8*); } class_definitions[i].klass = klass; class_definitions[i].class_byte_count = (jint) classFileBytesCount; class_definitions[i].class_bytes = classFileBytes; } omrthread_monitor_exit(classSegments->segmentMutex); if (rc == JVMTI_ERROR_NONE) { rc = redefineClassesCommon(env, class_count, class_definitions, currentThread, J9_FINDCLASS_FLAG_RETRANSFORMING); } /* free classFileBytes returned by j9bcutil_transformROMClass */ for (i = 0; i < class_count; ++i) { if (NULL != class_definitions[i].class_bytes) { J9Class * clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, classes[i]); if (!J9ROMCLASS_IS_INTERMEDIATE_DATA_A_CLASSFILE(clazz->romClass)) { j9mem_free_memory((void *)class_definitions[i].class_bytes); } } } j9mem_free_memory(class_definitions); } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } omrthread_monitor_exit(jvmtiData->redefineMutex); TRACE_JVMTI_RETURN(jvmtiRetransformClasses); } jvmtiError JNICALL jvmtiIsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_modifiable_class_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jboolean rv_is_modifiable = JNI_FALSE; Trc_JVMTI_jvmtiIsModifiableClass_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class * clazz; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_JCLASS(currentThread, klass); ENSURE_NON_NULL(is_modifiable_class_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); if (NULL == clazz) { JVMTI_ERROR(JVMTI_ERROR_INVALID_CLASS); } rv_is_modifiable = classIsModifiable(vm, clazz); if (rv_is_modifiable) { if (((J9JVMTIEnv *) env)->capabilities.can_retransform_classes) { J9MemorySegmentList * classSegments = vm->classMemorySegments; omrthread_monitor_enter(classSegments->segmentMutex); rv_is_modifiable = (WSRP_GET(clazz->romClass->intermediateClassData, U_8*) != NULL); omrthread_monitor_exit(classSegments->segmentMutex); } } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != is_modifiable_class_ptr) { *is_modifiable_class_ptr = rv_is_modifiable; } TRACE_JVMTI_RETURN(jvmtiIsModifiableClass); } jvmtiError JNICALL jvmtiGetClassVersionNumbers(jvmtiEnv* env, jclass klass, jint* minor_version_ptr, jint* major_version_ptr) { J9JavaVM * vm = JAVAVM_FROM_ENV(env); jvmtiError rc; J9VMThread * currentThread; jint rv_minor_version = 0; jint rv_major_version = 0; Trc_JVMTI_jvmtiGetClassVersionNumbers_Entry(env); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class* clazz; J9ROMClass * romClass; vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_JCLASS_NON_NULL(klass); ENSURE_NON_NULL(minor_version_ptr); ENSURE_NON_NULL(major_version_ptr); ENSURE_JCLASS(currentThread, klass); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); romClass = clazz->romClass; if (J9ROMCLASS_IS_PRIMITIVE_OR_ARRAY(romClass)) { rc = JVMTI_ERROR_ABSENT_INFORMATION; } else { rv_minor_version = (jint) romClass->minorVersion; rv_major_version = (jint) romClass->majorVersion; } done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); } if (NULL != minor_version_ptr) { *minor_version_ptr = rv_minor_version; } if (NULL != major_version_ptr) { *major_version_ptr = rv_major_version; } TRACE_JVMTI_RETURN(jvmtiGetClassVersionNumbers); } #define GCP_WRITE_U8(dst, src) \ do { \ *(dst) = src; \ dst++; \ } while (0) #ifdef J9VM_ENV_LITTLE_ENDIAN #define GCP_WRITE_U16(dst, src) \ do { \ dst[0] = ((U_8 *) &src)[1]; \ dst[1] = ((U_8 *) &src)[0]; \ dst += 2; \ } while (0) #else #define GCP_WRITE_U16(dst, src) \ do { \ *((U_16 *) dst) = (src); \ dst += 2; \ } while (0) #endif #ifdef J9VM_ENV_LITTLE_ENDIAN #define GCP_WRITE_U32(dst, src) \ do { \ dst[0] = ((U_8 *) &src)[3]; \ dst[1] = ((U_8 *) &src)[2]; \ dst[2] = ((U_8 *) &src)[1]; \ dst[3] = ((U_8 *) &src)[0]; \ dst += 4; \ } while (0) #else #define GCP_WRITE_U32(dst, src) \ do { \ *((U_32 *) dst) = (src); \ dst += 4; \ } while (0) #endif #ifdef JVMTI_GCP_DEBUG #define jvmtiGetConstantPoolTranslate_printf(args) printf args #define jvmtiGetConstantPoolWrite_printf(args) printf args #else #define jvmtiGetConstantPoolTranslate_printf(args) #define jvmtiGetConstantPoolWrite_printf(args) #endif /** * \brief Return the raw Constant Pool bytes for the specified class * \ingroup jvmtiClass * * * @param env JVMTI environment * @param klass The class to query * @param constant_pool_count_ptr On return, points to the number of entries in the constant pool table plus one. * @param constant_pool_byte_count_ptr On return, points to the number of bytes in the returned raw constant pool. * @param constant_pool_bytes_ptr On return, points to the raw constant pool, that is the bytes defined by the constant_pool * item of the Class File Format. Agent passes a pointer to a unsigned char*. On return, the * unsigned char* points to a newly allocated array of size *constant_pool_byte_count_ptr. * The array should be freed with Deallocate. * @return a jvmtiError code * * The call GetConstantPool call is described in detail by the JVMTI1.1 spec: * http://www.icogitate.com/~jsr163/eg/jvmti.html#GetConstantPool * */ jvmtiError JNICALL jvmtiGetConstantPool(jvmtiEnv* env, jclass klass, jint* constant_pool_count_ptr, jint* constant_pool_byte_count_ptr, unsigned char** constant_pool_bytes_ptr) { jvmtiError rc; J9JavaVM * vm = JAVAVM_FROM_ENV(env); J9VMThread * currentThread; jvmtiGcp_translation translation; unsigned char *constantPoolBuf; jint rv_constant_pool_count = 0; jint rv_constant_pool_byte_count = 0; unsigned char *rv_constant_pool_bytes = NULL; Trc_JVMTI_jvmtiGetConstantPool_Entry(env); memset(&translation, 0x00, sizeof(jvmtiGcp_translation)); rc = getCurrentVMThread(vm, ¤tThread); if (rc == JVMTI_ERROR_NONE) { J9Class* clazz; PORT_ACCESS_FROM_JAVAVM(vm); vm->internalVMFunctions->internalEnterVMFromJNI(currentThread); ENSURE_PHASE_START_OR_LIVE(env); ENSURE_CAPABILITY(env, can_get_constant_pool); ENSURE_JCLASS_NON_NULL(klass); ENSURE_JCLASS(currentThread, klass); ENSURE_NON_NULL(constant_pool_count_ptr); ENSURE_NON_NULL(constant_pool_byte_count_ptr); ENSURE_NON_NULL(constant_pool_bytes_ptr); clazz = J9VM_J9CLASS_FROM_JCLASS(currentThread, klass); /* Return an error for Array or Primitive classes */ if (J9ROMCLASS_IS_PRIMITIVE_OR_ARRAY(clazz->romClass)) { rc = JVMTI_ERROR_ABSENT_INFORMATION; JVMTI_ERROR(rc); } /* Figure out how much space we'll need on the return buffer and store translation * entries for all CP types. The translation is performed to make sure the CP indices * match those used by the bytecodes returned by GetBytecodes jvmti call. */ rc = jvmtiGetConstantPool_translateCP(PORTLIB, &translation, clazz, TRUE); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } /* Allocate the return buffer */ constantPoolBuf = j9mem_allocate_memory(translation.totalSize, J9MEM_CATEGORY_JVMTI_ALLOCATE); if (constantPoolBuf == NULL) { JVMTI_ERROR(JVMTI_ERROR_OUT_OF_MEMORY); } /* Write out all constants */ rc = jvmtiGetConstantPool_writeConstants(&translation, constantPoolBuf); if (rc != JVMTI_ERROR_NONE) { j9mem_free_memory(constantPoolBuf); JVMTI_ERROR(rc); } rv_constant_pool_count = translation.cpSize; rv_constant_pool_byte_count = translation.totalSize; rv_constant_pool_bytes = constantPoolBuf; done: vm->internalVMFunctions->internalExitVMToJNI(currentThread); jvmtiGetConstantPool_free(PORTLIB, &translation); } if (NULL != constant_pool_count_ptr) { *constant_pool_count_ptr = rv_constant_pool_count; } if (NULL != constant_pool_byte_count_ptr) { *constant_pool_byte_count_ptr = rv_constant_pool_byte_count; } if (NULL != constant_pool_bytes_ptr) { *constant_pool_bytes_ptr = rv_constant_pool_bytes; } TRACE_JVMTI_RETURN(jvmtiGetConstantPool); } /** * \brief Translate constant pool indices * \ingroup jvmtiClass * * @param privatePortLibrary port library * @param translation a data structure holding the translation HashTable and Array. * This call will allocate a HashTable and an array that must be * freed via the jvmtiGetConstantPool_free() call once done. * @param class class to be used as the constant pool source * @param translateUTF8andNAS the translated constant pool should include UTF8 and Name and Type * constants. A distinction is made since when used by GetBytecodes * we do not care about those two constant types. * @return a jvmtiError code * * * This call is used by the GetConstantPool and GetBytecodes jvmti API calls. It serves as * means of obtaining identical constant pool indices for the said two calls. * * To achieve this it provides a HashTable of unique constant pool entries. The HashTable is * keyed by the j9vm constant pool index for all items except UTF8 and NameAndSignature. The two * prior items are not directly present on J9ROMClass CP and are instead keyed by their SRPs. * The HashTable is used by the GetBytecodes call. It is also utilized while reindexing the * constant pool. * * The translation structure also provides an array of HashTable nodes. The array is indexed * by the 'reindexed' constant pool indices. It provides efficient means of writing out the * constant pool by the jvmtiGetConstantPool_writeConstants function. * * ISSUES: * The UTF8 and NameAndSignature constants are not stored on the constant pool and * therefore do not have an "index" but rather use SRP references. This call will * create CP entries and update referring CP items accordingly * * The Long and Double type is defined by the spec to take _TWO_ constant pool entries * instead of one. This creates a problem since our bytecode's cp indices have been * rewritten to get rid of the extra CP index. The returned constant pool is written * out containing that extra index. This in turn forced us to change the GetBytecodes * jvmti call to reindex all bytecodes using the CP to account for that such that the * bytecodes returned by it match the constant pool returned here. * * The first 255 CP items must not be reindexed to an index greater then 255. Breaking * that rule will cause LDC bytecode breakage as it uses a 1 byte constant pool index * that cannot be fixed if the referenced CP item is out of range. To address this issue * we first write out the J9RomClass constant pool and then append UTF8 and NameAndType * items. This ensures that whatever items were indexed below 255 still remain so. * * NOTE: * Caller is responsible for freeing the hashtable and cp array tracked by the translation * structure by calling the jvmtiGetConstantPool_free API call. * */ jvmtiError jvmtiGetConstantPool_translateCP(J9PortLibrary *privatePortLibrary, jvmtiGcp_translation *translation, J9Class *class, jboolean translateUTF8andNAS) { U_32 sunCpIndex = 1; UDATA cpIndex, i; U_8 cpItemType; J9ROMConstantPoolItem *cpItem; jvmtiError rc = JVMTI_ERROR_NONE; jvmtiGcp_translationEntry *htEntry; J9ROMClass *romClass; J9ROMConstantPoolItem *constantPool; U_32 constantPoolCount; U_32 * cpShapeDescription; romClass = class->romClass; constantPool = (J9ROMConstantPoolItem*) ((U_8*) romClass + sizeof(J9ROMClass)); constantPoolCount = romClass->romConstantPoolCount; cpShapeDescription = J9ROMCLASS_CPSHAPEDESCRIPTION(romClass); /* Clear and initialize the translation struct */ memset(translation, 0x00, sizeof(struct jvmtiGcp_translation)); translation->romConstantPool = constantPool; /* Allocate the Hash Table used to store CP entries. */ translation->ht = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 256, sizeof(jvmtiGcp_translationEntry), 0, 0, J9MEM_CATEGORY_JVMTI, jvmtiGetConstantPool_HashFn, jvmtiGetConstantPool_HashEqualFn, NULL, NULL); if (NULL == translation->ht) { JVMTI_ERROR(JVMTI_ERROR_OUT_OF_MEMORY); } /* Allocate an array of hash node ptrs. The size is an upper bound on the number of CP items given the * constantPoolCount. ie the max number of items we can have is if they were all Reference types. * We allocate more in order to prevent a second pass after the real total number of CP items is calculated and * the HashTable populated. Instead we initialize the lookup array at the same time as creating the HT. */ /* The "6" is calculated by taking a worst case of lets say a FieldRef and counting the max number of * unique items used to completely describe it */ translation->cp = j9mem_allocate_memory((constantPoolCount * 6) * sizeof(jvmtiGcp_translationEntry *), J9MEM_CATEGORY_JVMTI); if (NULL == translation->cp) { JVMTI_ERROR(JVMTI_ERROR_OUT_OF_MEMORY); } jvmtiGetConstantPoolTranslate_printf(("JVMTI GetConstantPool: Creating Translation Table\n")); for (cpIndex = 1; cpIndex < constantPoolCount; cpIndex++) { cpItem = &constantPool[cpIndex]; cpItemType = J9_CP_TYPE(cpShapeDescription, cpIndex); jvmtiGetConstantPoolTranslate_printf(("S %2d/%d vmCPType: %d\n", cpIndex, constantPoolCount, cpItemType)); switch(cpItemType) { case J9CPTYPE_CLASS: case J9CPTYPE_STRING: case J9CPTYPE_ANNOTATION_UTF8: rc = jvmtiGetConstantPool_addClassOrString(translation, cpIndex, (U_8) ((cpItemType == J9CPTYPE_CLASS) ? CFR_CONSTANT_Class : CFR_CONSTANT_String), J9ROMSTRINGREF_UTF8DATA((J9ROMStringRef *) cpItem), &sunCpIndex, NULL); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9CPTYPE_FIELD: rc = jvmtiGetConstantPool_addReference(translation, cpIndex, (U_8)CFR_CONSTANT_Fieldref, (J9ROMFieldRef *) cpItem, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9CPTYPE_HANDLE_METHOD: case J9CPTYPE_INSTANCE_METHOD: case J9CPTYPE_STATIC_METHOD: case J9CPTYPE_INTERFACE_METHOD: case J9CPTYPE_INTERFACE_INSTANCE_METHOD: case J9CPTYPE_INTERFACE_STATIC_METHOD: { U_8 cpType = (U_8)CFR_CONSTANT_Methodref; if ((cpItemType == J9CPTYPE_INTERFACE_METHOD) || (cpItemType == J9CPTYPE_INTERFACE_STATIC_METHOD) || (cpItemType == J9CPTYPE_INTERFACE_INSTANCE_METHOD) ) { cpType = (U_8)CFR_CONSTANT_InterfaceMethodref; } rc = jvmtiGetConstantPool_addReference(translation, cpIndex, cpType, (J9ROMFieldRef *) cpItem, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; } case J9CPTYPE_METHOD_TYPE: rc = jvmtiGetConstantPool_addMethodType(translation, cpIndex, (U_8) CFR_CONSTANT_MethodType, (J9ROMMethodTypeRef *) cpItem, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9CPTYPE_METHODHANDLE: rc = jvmtiGetConstantPool_addMethodHandle(translation, cpIndex, (U_8) CFR_CONSTANT_MethodHandle, (J9ROMMethodHandleRef *) cpItem, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9CPTYPE_INT: case J9CPTYPE_FLOAT: rc = jvmtiGetConstantPool_addIntFloat(translation, cpIndex, (U_8) ((cpItemType == J9CPTYPE_INT) ? CFR_CONSTANT_Integer : CFR_CONSTANT_Float), ((J9ROMSingleSlotConstantRef *) cpItem)->data, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9CPTYPE_LONG: case J9CPTYPE_DOUBLE: rc = jvmtiGetConstantPool_addLongDouble(translation, cpIndex, (U_8) ((cpItemType == J9CPTYPE_DOUBLE) ? CFR_CONSTANT_Double : CFR_CONSTANT_Long), ((J9ROMConstantRef *) cpItem)->slot1, ((J9ROMConstantRef *) cpItem)->slot2, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; default: jvmtiGetConstantPoolTranslate_printf((" Unknown cpType %2d\n", cpItemType)); JVMTI_ERROR(JVMTI_ERROR_INTERNAL); } } /* Check if we have a CallSiteTable and map the values back to CONSTANT_InvokeDynamics */ if (romClass->callSiteCount > 0) { J9SRP *callSiteData = (J9SRP *) J9ROMCLASS_CALLSITEDATA(romClass); U_16 *bsmIndices = (U_16 *) (callSiteData + romClass->callSiteCount); UDATA callSiteIndex; for (callSiteIndex = 0; callSiteIndex < romClass->callSiteCount; callSiteIndex++) { J9ROMNameAndSignature* nameAndSig = SRP_PTR_GET(callSiteData + callSiteIndex, J9ROMNameAndSignature*); U_16 bsmIndex = bsmIndices[callSiteIndex]; rc = jvmtiGetConstantPool_addInvokeDynamic(translation, (UDATA) callSiteData + callSiteIndex, /* Use the SRP as the hashtable key */ nameAndSig, bsmIndex, &sunCpIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } } } /* Add UTF8 or NameAndType items at the end and fix late bindings */ if (translateUTF8andNAS) { translation->cpSize = sunCpIndex; for (i = 1; i < translation->cpSize; i++) { J9UTF8 *utf8; J9ROMNameAndSignature *nas; J9ROMFieldRef *ref; htEntry = translation->cp[i]; /* Skip the NULL pads that follow Double/Long entries */ if (NULL == htEntry) continue; cpItemType = htEntry->cpType; switch (htEntry->cpType) { case CFR_CONSTANT_Class: case CFR_CONSTANT_String: /* Add and Bind the UTF8 used by this Class or String item */ rc = jvmtiGetConstantPool_addUTF8(translation, htEntry->type.clazz.utf8, &sunCpIndex, &htEntry->type.clazz.nameIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case CFR_CONSTANT_Fieldref: case CFR_CONSTANT_Methodref: case CFR_CONSTANT_InterfaceMethodref: /* Fix the Reference by adding the UTF8 and NameAndSignature items to the hashtable and binding * the indices to this ref */ ref = htEntry->type.ref.ref; nas = J9ROMFIELDREF_NAMEANDSIGNATURE(ref); utf8 = J9ROMSTRINGREF_UTF8DATA((J9ROMStringRef *) &translation->romConstantPool[ref->classRefCPIndex]); jvmtiGetConstantPoolTranslate_printf((" Ref : Class [%*s] NAS [%*s][%*s]\n", J9UTF8_LENGTH(utf8), J9UTF8_DATA(utf8), J9UTF8_LENGTH(J9ROMNAMEANDSIGNATURE_NAME(nas)), J9UTF8_DATA(J9ROMNAMEANDSIGNATURE_NAME(nas)), J9UTF8_LENGTH(J9ROMNAMEANDSIGNATURE_SIGNATURE(nas)), J9UTF8_DATA(J9ROMNAMEANDSIGNATURE_SIGNATURE(nas)))); /* Add the referenced Class item to the HT, we explicitly do it here in case the refered class * has not yet been added. Deferring it to be done via the CFR_CONSTANT_Class case would * prevent us from being able to save the index in htEntry->type.ref.classIndex (ie another * pass would be needed once the CFR_CONSTANT_Class case adds it) */ rc = jvmtiGetConstantPool_addClassOrString(translation, ref->classRefCPIndex, (U_8)CFR_CONSTANT_Class, utf8, &sunCpIndex, &(htEntry->type.ref.classIndex)); if (rc != JVMTI_ERROR_NONE) { return rc; } /* Add the referenced NameAndSignature item to the HT */ rc = jvmtiGetConstantPool_addNAS(translation, nas, &sunCpIndex, &htEntry->type.ref.nameAndTypeIndex); if (rc != JVMTI_ERROR_NONE) { return rc; } break; case CFR_CONSTANT_InvokeDynamic: /* Fix the invokedynamic by adding the NameAndSignature to the hashtable and binding in the index */ nas = htEntry->type.invokedynamic.nas; jvmtiGetConstantPoolTranslate_printf((" invokedynamic : bsmIndex [%i] NAS [%*s][%*s]\n", htEntry->type.invokedynamic.bsmIndex, J9UTF8_LENGTH(J9ROMNAMEANDSIGNATURE_NAME(nas)), J9UTF8_DATA(J9ROMNAMEANDSIGNATURE_NAME(nas)), J9UTF8_LENGTH(J9ROMNAMEANDSIGNATURE_SIGNATURE(nas)), J9UTF8_DATA(J9ROMNAMEANDSIGNATURE_SIGNATURE(nas)))); /* Add the referenced NameAndSignature item to the HT */ rc = jvmtiGetConstantPool_addNAS(translation, nas, &sunCpIndex, &htEntry->type.invokedynamic.nameAndTypeIndex); if (rc != JVMTI_ERROR_NONE) { return rc; } break; case CFR_CONSTANT_MethodType: { U_16 origin = htEntry->type.methodType.methodType->cpType >> J9DescriptionCpTypeShift; switch(origin) { case J9_METHOD_TYPE_ORIGIN_LDC: /* Add and Bind the UTF8 used by this MethodType item */ rc = jvmtiGetConstantPool_addUTF8(translation, htEntry->type.methodType.utf8, &sunCpIndex, &htEntry->type.methodType.methodTypeIndex); if (rc != JVMTI_ERROR_NONE) { JVMTI_ERROR(rc); } break; case J9_METHOD_TYPE_ORIGIN_INVOKE: case J9_METHOD_TYPE_ORIGIN_INVOKE_EXACT: /* Add the referenced Class item to the HT, we explicitly do it here in case the referred class * has not yet been added. Deferring it to be done via the CFR_CONSTANT_Class case would * prevent us from being able to save the index in htEntry->type.methodType.classIndex (ie another * pass would be needed once the CFR_CONSTANT_Class case adds it) */ rc = jvmtiGetConstantPool_addClassOrString(translation, (UDATA)&MH_methodHandle_utf, (U_8)CFR_CONSTANT_Class, (J9UTF8 *)&MH_methodHandle_utf, &sunCpIndex, &(htEntry->type.methodType.classIndex)); if (rc != JVMTI_ERROR_NONE) { return rc; } /* Add the referenced NameAndSignature item to the HT */ rc = jvmtiGetConstantPool_addNAS_name_sig(translation, (void *)(((U_8 *)htEntry->type.methodType.utf8) + 1), /* Key for this is 1 off the sig utf8 */ (origin == J9_METHOD_TYPE_ORIGIN_INVOKE_EXACT ? (J9UTF8 *)&MH_invokeExact_utf : (J9UTF8 *)&MH_invoke_utf), htEntry->type.methodType.utf8, &sunCpIndex, &htEntry->type.ref.nameAndTypeIndex); if (rc != JVMTI_ERROR_NONE) { return rc; } break; } } break; case CFR_CONSTANT_MethodHandle: /* Nothing to do: no UTF8s to bind */ break; case CFR_CONSTANT_Integer: case CFR_CONSTANT_Float: case CFR_CONSTANT_Double: case CFR_CONSTANT_Long: break; default: jvmtiGetConstantPoolTranslate_printf((" Unknown cpType %2d\n", cpItemType)); JVMTI_ERROR(JVMTI_ERROR_INTERNAL); } } } translation->cpSize = sunCpIndex; return rc; done: /* Error handling path */ jvmtiGetConstantPool_free(privatePortLibrary, translation); return rc; } /** * \brief Free the translation HashTable and Array * \ingroup jvmtiClass * * * @param privatePortLibrary port library * @param translation constant pool translation data * */ void jvmtiGetConstantPool_free(J9PortLibrary *privatePortLibrary, jvmtiGcp_translation *translation) { if (translation->ht) { hashTableFree(translation->ht); translation->ht = NULL; } if (translation->cp) { j9mem_free_memory(translation->cp); translation->cp = NULL; } } /** * \brief Write the translated constant pool in SUN byte packed format * \ingroup jvmtiClass * * * @param translation an initialized translation structure * @param constantPoolBuf the return buffer to contain the written constant pool bytes * @return a jvmtiError code * * Use the translation data gathered by jvmtiGetConstantPool_translateCP to create a byte packed * constant pool stream. The constant pool conforms to the SUN classfile specification and must * match with the indices used by the bytecodes returned by the GetBytecodes jvmti API call. * */ static jvmtiError jvmtiGetConstantPool_writeConstants(jvmtiGcp_translation *translation, unsigned char *constantPoolBuf) { jvmtiError rc = JVMTI_ERROR_NONE; U_16 sunCpIndex; U_8 cpItemType; jvmtiGcp_translationEntry *htEntry; unsigned char * constantPoolBufIndex; jvmtiGetConstantPoolWrite_printf(("============ JVMTI GetConstantPool: Writing Constant Pool ===============\n")); constantPoolBufIndex = constantPoolBuf; for (sunCpIndex = 1; sunCpIndex < translation->cpSize; sunCpIndex++) { htEntry = translation->cp[sunCpIndex]; cpItemType = htEntry->cpType; jvmtiGetConstantPoolWrite_printf(("W %2d/%d type: %2d [%3d/%3d] --- ", sunCpIndex, translation->cpSize, cpItemType, constantPoolBufIndex - constantPoolBuf, translation->totalSize)); switch(cpItemType) { case CFR_CONSTANT_Class: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <Class> UTF8 %d->[%s]\n", htEntry->cpType, htEntry->type.clazz.nameIndex, "")); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Class); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.clazz.nameIndex); break; case CFR_CONSTANT_Fieldref: case CFR_CONSTANT_Methodref: case CFR_CONSTANT_InterfaceMethodref: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <Ref> Class %d NAS %d\n", htEntry->cpType, htEntry->type.ref.classIndex, htEntry->type.ref.nameAndTypeIndex)); GCP_WRITE_U8 (constantPoolBufIndex, cpItemType); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.ref.classIndex); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.ref.nameAndTypeIndex); break; case CFR_CONSTANT_MethodHandle: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <MethodHandle> method/fieldIndex %d refType %d\n", htEntry->cpType, htEntry->type.methodHandle.methodOrFieldRefIndex, htEntry->type.methodHandle.handleType)); GCP_WRITE_U8 (constantPoolBufIndex, cpItemType); GCP_WRITE_U8 (constantPoolBufIndex, htEntry->type.methodHandle.handleType); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.methodHandle.methodOrFieldRefIndex); break; case CFR_CONSTANT_InvokeDynamic: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <InvokeDynamic> bsmIndex %d NAS %d\n", htEntry->cpType, htEntry->type.invokedynamic.bsmIndex, htEntry->type.invokedynamic.nameAndTypeIndex)); GCP_WRITE_U8 (constantPoolBufIndex, cpItemType); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.invokedynamic.bsmIndex); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.invokedynamic.nameAndTypeIndex); break; case CFR_CONSTANT_String: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <String> UTF8 %d->[%s]\n", htEntry->cpType, htEntry->type.string.stringIndex, "")); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_String); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.string.stringIndex); break; case CFR_CONSTANT_MethodType: if (J9_METHOD_TYPE_ORIGIN_LDC == (htEntry->type.methodType.methodType->cpType >> J9DescriptionCpTypeShift)) { jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <MethodType> UTF8 %d->[%s]\n", htEntry->cpType, htEntry->type.methodType.methodTypeIndex, "")); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_MethodType); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.methodType.methodTypeIndex); } else { /* invokehandle or invokehandlegeneric: MethodType mapped to MethodRef */ jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <MethodRef from MethodType> UTF8 %d->[%s]\n", htEntry->cpType, htEntry->type.methodType.methodTypeIndex, "")); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Methodref); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.methodType.classIndex); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.methodType.nameAndTypeIndex); } break; case CFR_CONSTANT_Integer: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <Int> [0x%04x]\n", htEntry->cpType, htEntry->type.intFloat.data)); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Integer); GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.intFloat.data); break; case CFR_CONSTANT_Float: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <Float> [0x%04x]\n", htEntry->cpType, htEntry->type.intFloat.data)); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Float); GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.intFloat.data); break; case CFR_CONSTANT_Double: case CFR_CONSTANT_Long: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <Double/Long> [0x%04x%04x]\n", htEntry->cpType, htEntry->type.longDouble.slot1, htEntry->type.longDouble.slot2)); if (cpItemType == CFR_CONSTANT_Double) { GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Double); } else { GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Long); } #ifdef J9VM_ENV_LITTLE_ENDIAN GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.longDouble.slot2); GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.longDouble.slot1); #else GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.longDouble.slot1); GCP_WRITE_U32(constantPoolBufIndex, htEntry->type.longDouble.slot2); #endif /* Skip additional CP entry. See JVM spec v2 section 4.4.5 for the brilliant rationale */ sunCpIndex++; break; case CFR_CONSTANT_Utf8: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <UTF8> [%*s]\n", htEntry->cpType, J9UTF8_LENGTH(htEntry->type.utf8.data), J9UTF8_DATA(htEntry->type.utf8.data))); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_Utf8); GCP_WRITE_U16(constantPoolBufIndex, J9UTF8_LENGTH(htEntry->type.utf8.data)); memcpy(constantPoolBufIndex, J9UTF8_DATA(htEntry->type.utf8.data), J9UTF8_LENGTH(htEntry->type.utf8.data)); constantPoolBufIndex += J9UTF8_LENGTH(htEntry->type.utf8.data); break; case CFR_CONSTANT_NameAndType: jvmtiGetConstantPoolWrite_printf((" HT CPT %2d <NAS> Name %d->[%s] Sig %d->[%s]\n", htEntry->cpType, htEntry->type.nas.nameIndex, "", htEntry->type.nas.signatureIndex, "")); GCP_WRITE_U8 (constantPoolBufIndex, CFR_CONSTANT_NameAndType); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.nas.nameIndex); GCP_WRITE_U16(constantPoolBufIndex, htEntry->type.nas.signatureIndex); break; default: JVMTI_ERROR(JVMTI_ERROR_INTERNAL); break; } } done: /* Belt and suspenders, check if we have not overflown the return buffer */ if ((U_32) (constantPoolBufIndex - constantPoolBuf) > translation->totalSize) { jvmtiGetConstantPoolWrite_printf(("ERROR: Constant Pool buffer Overflow %d > %d\n", constantPoolBufIndex - constantPoolBuf, translation->totalSize)); rc = JVMTI_ERROR_INTERNAL; } return rc; } static jvmtiError jvmtiGetConstantPool_addClassOrString(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9UTF8 *utf8, U_32 *sunCpIndex, U_32 *refIndex) { jvmtiGcp_translationEntry entry; jvmtiGcp_translationEntry *htEntry; /* Add the Class item to the hashtable, use our CP index as key */ entry.key = (void *) cpIndex; if (NULL != (htEntry = hashTableFind(translation->ht, &entry))) { /* Found a match, we've already added it before hence return to prevent * unnecessary duplication */ if (refIndex) *refIndex = htEntry->sunCpIndex; return JVMTI_ERROR_NONE; } entry.cpType = cpType; entry.sunCpIndex = *sunCpIndex; if (cpType == CFR_CONSTANT_Class) { entry.type.clazz.nameIndex = 0; entry.type.clazz.utf8 = utf8; } else { entry.type.string.stringIndex = 0; entry.type.string.utf8 = utf8; } if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } if (refIndex) { *refIndex = *sunCpIndex; } translation->cp[*sunCpIndex] = htEntry; translation->totalSize += 3; (*sunCpIndex)++; jvmtiGetConstantPoolTranslate_printf((" Class|String : UTF8 [%*s]\n", J9UTF8_LENGTH(utf8), J9UTF8_DATA(utf8))); return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addMethodType(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMMethodTypeRef *ref, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry; jvmtiGcp_translationEntry *htEntry; J9UTF8 *utf8 = J9ROMMETHODTYPEREF_SIGNATURE(ref); jvmtiError rc; /* Add the MethodType item to the hashtable, use our CP index as key so that * jvmtiMethod.c:jvmtiGetBytecodes can map back to the right cpItem */ entry.key = (void *) cpIndex; if (NULL != (hashTableFind(translation->ht, &entry))) { return JVMTI_ERROR_NONE; } entry.cpType = cpType; entry.sunCpIndex = *sunCpIndex; entry.type.methodType.methodTypeIndex = 0; entry.type.methodType.utf8 = utf8; entry.type.methodType.methodType = ref; if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->cp[*sunCpIndex] = htEntry; if ((ref->cpType >> J9DescriptionCpTypeShift) == J9_METHOD_TYPE_ORIGIN_LDC) { translation->totalSize += 3; rc = JVMTI_ERROR_NONE; } else { /* invoke or invokeExact: therefore a methodref and needs 5 */ translation->totalSize += 5; /* also register an entry for the MethodHandle class: * use address of the MH_methodHandle_utf as the cpIndex aka key to hashtable */ rc = jvmtiGetConstantPool_addClassOrString(translation, (UDATA)&MH_methodHandle_utf, (U_8) CFR_CONSTANT_Class, (J9UTF8 *)&MH_methodHandle_utf, sunCpIndex, NULL); } (*sunCpIndex)++; jvmtiGetConstantPoolTranslate_printf((" MethodType : UTF8 [%*s]\n", J9UTF8_LENGTH(utf8), J9UTF8_DATA(utf8))); return rc; } static jvmtiError jvmtiGetConstantPool_addInvokeDynamic(jvmtiGcp_translation *translation, UDATA key, J9ROMNameAndSignature *nameAndSig, U_32 bsmIndex, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry; jvmtiGcp_translationEntry *htEntry; entry.key = (void *) key; if (NULL != (hashTableFind(translation->ht, &entry))) { return JVMTI_ERROR_NONE; } entry.cpType = CFR_CONSTANT_InvokeDynamic; entry.sunCpIndex = *sunCpIndex; entry.type.invokedynamic.bsmIndex = bsmIndex; entry.type.invokedynamic.nameAndTypeIndex = 0; entry.type.invokedynamic.nas = nameAndSig; if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->cp[*sunCpIndex] = htEntry; translation->totalSize += 5; (*sunCpIndex)++; jvmtiGetConstantPoolTranslate_printf((" InvokeDynamic : bsmIndex [%i] NaS [toBeTranslated]\n", bsmIndex)); return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addUTF8(jvmtiGcp_translation *translation, J9UTF8 *utf8, U_32 *sunCpIndex, U_32 *refIndex) { jvmtiGcp_translationEntry entry; jvmtiGcp_translationEntry *htEntry; entry.key = utf8; jvmtiGetConstantPoolTranslate_printf((" UTF8 [%*s]\n", J9UTF8_LENGTH(utf8), J9UTF8_DATA(utf8))); if (NULL != (htEntry = hashTableFind(translation->ht, &entry))) { /* Found a match, we've already added it before hence return to prevent * unnecessary duplication */ *refIndex = htEntry->sunCpIndex; return JVMTI_ERROR_NONE; } /* Store the new/translated CP index */ entry.sunCpIndex = *sunCpIndex; entry.cpType = CFR_CONSTANT_Utf8; entry.type.utf8.data = utf8; /* Add the UTF8 to the hashtable */ if (NULL == (translation->cp[*sunCpIndex] = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } *refIndex = *sunCpIndex; (*sunCpIndex)++; translation->totalSize += 3 + J9UTF8_LENGTH(utf8); return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addReference(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMFieldRef *ref, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry; jvmtiGcp_translationEntry *htEntry; /* Add the Reference item to the hashtable, use our CP index as key */ entry.key = (void *) cpIndex; entry.cpType = cpType; entry.sunCpIndex = *sunCpIndex; entry.type.ref.ref = ref; entry.type.ref.classIndex = 0; entry.type.ref.nameAndTypeIndex = 0; if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->totalSize += 5; translation->cp[*sunCpIndex] = htEntry; (*sunCpIndex)++; return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addMethodHandle(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, J9ROMMethodHandleRef *ref, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry = {0}; jvmtiGcp_translationEntry *htEntry = NULL; U_8 fieldOrMethodCpType = 0; /* Add the Reference item to the hashtable, use our CP index as key */ entry.key = (void *)cpIndex; entry.cpType = cpType; entry.sunCpIndex = *sunCpIndex; entry.type.methodHandle.methodOrFieldRefIndex = (*sunCpIndex) + 1; entry.type.methodHandle.handleType = ref->handleTypeAndCpType >> J9DescriptionCpTypeShift; if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->totalSize += 4; translation->cp[*sunCpIndex] = htEntry; (*sunCpIndex)++; switch(entry.type.methodHandle.handleType) { case MH_REF_GETFIELD: case MH_REF_PUTFIELD: case MH_REF_GETSTATIC: case MH_REF_PUTSTATIC: fieldOrMethodCpType = CFR_CONSTANT_Fieldref; break; case MH_REF_INVOKEVIRTUAL: case MH_REF_INVOKESTATIC: case MH_REF_INVOKESPECIAL: case MH_REF_NEWINVOKESPECIAL: fieldOrMethodCpType = CFR_CONSTANT_Methodref; break; case MH_REF_INVOKEINTERFACE: fieldOrMethodCpType = CFR_CONSTANT_InterfaceMethodref; break; default: Assert_JVMTI_unreachable(); return JVMTI_ERROR_INTERNAL; } /* Ensure the field/method ref is also added */ return jvmtiGetConstantPool_addReference( translation, ref->methodOrFieldRefIndex, fieldOrMethodCpType, (J9ROMFieldRef *)&translation->romConstantPool[ref->methodOrFieldRefIndex], sunCpIndex); } static jvmtiError jvmtiGetConstantPool_addNAS(jvmtiGcp_translation *translation, J9ROMNameAndSignature *nas, U_32 *sunCpIndex, U_32 *refIndex) { return jvmtiGetConstantPool_addNAS_name_sig(translation, nas, J9ROMNAMEANDSIGNATURE_NAME(nas), J9ROMNAMEANDSIGNATURE_SIGNATURE(nas), sunCpIndex, refIndex); } /* 292 MTs that convert to MethodRefs don't have a ROM NAS - just a static one in this file. This allows us to pass * all the right data in without having to determine if its a ROM NaS or static one. */ static jvmtiError jvmtiGetConstantPool_addNAS_name_sig(jvmtiGcp_translation *translation, void *key, J9UTF8 *name, J9UTF8* sig, U_32 *sunCpIndex, U_32 *refIndex) { jvmtiError rc; jvmtiGcp_translationEntry entry, *htEntry; entry.key = key; entry.sunCpIndex = *sunCpIndex; entry.cpType = CFR_CONSTANT_NameAndType; entry.type.nas.name = name; entry.type.nas.signature = sig; jvmtiGetConstantPoolTranslate_printf((" NAS [%*s][%*s]\n", J9UTF8_LENGTH(entry.type.nas.name), J9UTF8_DATA(entry.type.nas.name), J9UTF8_LENGTH(entry.type.nas.signature), J9UTF8_DATA(entry.type.nas.signature))); if (NULL != (htEntry = hashTableFind(translation->ht, &entry))) { /* Found a match, we've already added it before hence return to prevent * unnecessary duplication */ *refIndex = htEntry->sunCpIndex; return JVMTI_ERROR_NONE; } /* Add the NameAndSignature entry to the hashtable */ if (NULL == (htEntry = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->cp[*sunCpIndex] = htEntry; *refIndex = *sunCpIndex; (*sunCpIndex)++; /* Add an entry for the name_index part of the NAS */ rc = jvmtiGetConstantPool_addUTF8(translation, name, sunCpIndex, &htEntry->type.nas.nameIndex); if (rc != JVMTI_ERROR_NONE) { return rc; } /* Add an entry for the signature_index part of the NAS entry */ rc = jvmtiGetConstantPool_addUTF8(translation, sig, sunCpIndex, &htEntry->type.nas.signatureIndex); if (rc != JVMTI_ERROR_NONE) { return rc; } translation->totalSize += 5; return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addIntFloat(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, U_32 data, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry; jvmtiGetConstantPoolTranslate_printf((" Int/Float [0x%08x]\n", data)); entry.key = (void *) cpIndex; entry.sunCpIndex = *sunCpIndex; entry.cpType = cpType; entry.type.intFloat.data = data; /* Add the entry to the hashtable */ if (NULL == (translation->cp[*sunCpIndex] = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } (*sunCpIndex)++; translation->totalSize += 5; return JVMTI_ERROR_NONE; } static jvmtiError jvmtiGetConstantPool_addLongDouble(jvmtiGcp_translation *translation, UDATA cpIndex, U_8 cpType, U_32 slot1, U_32 slot2, U_32 *sunCpIndex) { jvmtiGcp_translationEntry entry; jvmtiGetConstantPoolTranslate_printf((" Long/Double [0x%08x%08x]\n", slot1, slot2)); entry.key = (void *) cpIndex; entry.sunCpIndex = *sunCpIndex; entry.cpType = cpType; entry.type.longDouble.slot1 = slot1; entry.type.longDouble.slot2 = slot2; /* Add the entry to the hashtable */ if (NULL == (translation->cp[*sunCpIndex] = hashTableAdd(translation->ht, &entry))) { return JVMTI_ERROR_OUT_OF_MEMORY; } translation->cp[*sunCpIndex + 1] = NULL; (*sunCpIndex) += 2; translation->totalSize += 9; return JVMTI_ERROR_NONE; } static UDATA jvmtiGetConstantPool_HashFn(void *entry, void *userData) { jvmtiGcp_translationEntry *key = (jvmtiGcp_translationEntry *) entry; return (UDATA) key->key; } static UDATA jvmtiGetConstantPool_HashEqualFn(void *lhsEntry, void *rhsEntry, void *userData) { jvmtiGcp_translationEntry *e1 = (jvmtiGcp_translationEntry *) lhsEntry; jvmtiGcp_translationEntry *e2 = (jvmtiGcp_translationEntry *) rhsEntry; return (e1->key == e2->key); }