static ClassFileStream* prologue(ClassFileStream* stream, Symbol* name, ClassLoaderData* loader_data, Handle protection_domain, JvmtiCachedClassFileData** cached_class_file, TRAPS) { assert(stream != NULL, "invariant"); if (JvmtiExport::should_post_class_file_load_hook()) { assert(THREAD->is_Java_thread(), "must be a JavaThread"); const JavaThread* jt = (JavaThread*)THREAD; Handle class_loader(THREAD, loader_data->class_loader()); // Get the cached class file bytes (if any) from the class that // is being redefined or retransformed. We use jvmti_thread_state() // instead of JvmtiThreadState::state_for(jt) so we don't allocate // a JvmtiThreadState any earlier than necessary. This will help // avoid the bug described by 7126851. JvmtiThreadState* state = jt->jvmti_thread_state(); if (state != NULL) { KlassHandle* h_class_being_redefined = state->get_class_being_redefined(); if (h_class_being_redefined != NULL) { instanceKlassHandle ikh_class_being_redefined = instanceKlassHandle(THREAD, (*h_class_being_redefined)()); *cached_class_file = ikh_class_being_redefined->get_cached_class_file(); } } unsigned char* ptr = const_cast<unsigned char*>(stream->buffer()); unsigned char* end_ptr = ptr + stream->length(); JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain, &ptr, &end_ptr, cached_class_file); if (ptr != stream->buffer()) { // JVMTI agent has modified class file data. // Set new class file stream using JVMTI agent modified class file data. stream = new ClassFileStream(ptr, end_ptr - ptr, stream->source(), stream->need_verify()); } } return stream; }
void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { ((methodOopDesc*)_method->*meth_act)(_bci); // add/remove breakpoint to/from versions of the method that // are EMCP. Directly or transitively obsolete methods are // not saved in the PreviousVersionInfo. Thread *thread = Thread::current(); instanceKlassHandle ikh = instanceKlassHandle(thread, _method->method_holder()); Symbol* m_name = _method->name(); Symbol* m_signature = _method->signature(); { ResourceMark rm(thread); // PreviousVersionInfo objects returned via PreviousVersionWalker // contain a GrowableArray of handles. We have to clean up the // GrowableArray _after_ the PreviousVersionWalker destructor // has destroyed the handles. { // search previous versions if they exist PreviousVersionWalker pvw((instanceKlass *)ikh()->klass_part()); for (PreviousVersionInfo * pv_info = pvw.next_previous_version(); pv_info != NULL; pv_info = pvw.next_previous_version()) { GrowableArray<methodHandle>* methods = pv_info->prev_EMCP_method_handles(); if (methods == NULL) { // We have run into a PreviousVersion generation where // all methods were made obsolete during that generation's // RedefineClasses() operation. At the time of that // operation, all EMCP methods were flushed so we don't // have to go back any further. // // A NULL methods array is different than an empty methods // array. We cannot infer any optimizations about older // generations from an empty methods array for the current // generation. break; } for (int i = methods->length() - 1; i >= 0; i--) { methodHandle method = methods->at(i); if (method->name() == m_name && method->signature() == m_signature) { RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)", meth_act == &methodOopDesc::set_breakpoint ? "sett" : "clear", method->name()->as_C_string(), method->signature()->as_C_string())); assert(!method->is_obsolete(), "only EMCP methods here"); ((methodOopDesc*)method()->*meth_act)(_bci); break; } } } } // pvw is cleaned up } // rm is cleaned up }
instanceKlassHandle record_result(const int classpath_index, const ClassPathEntry* e, instanceKlassHandle result, TRAPS) { if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) { if (DumpSharedSpaces) { result->set_shared_classpath_index(classpath_index); } return result; } else { return instanceKlassHandle(); // NULL } }
void JvmtiBreakpoint::each_method_version_do(method_action meth_act) { ((Method*)_method->*meth_act)(_bci); // add/remove breakpoint to/from versions of the method that // are EMCP. Directly or transitively obsolete methods are // not saved in the PreviousVersionNodes. Thread *thread = Thread::current(); instanceKlassHandle ikh = instanceKlassHandle(thread, _method->method_holder()); Symbol* m_name = _method->name(); Symbol* m_signature = _method->signature(); // search previous versions if they exist PreviousVersionWalker pvw(thread, (InstanceKlass *)ikh()); for (PreviousVersionNode * pv_node = pvw.next_previous_version(); pv_node != NULL; pv_node = pvw.next_previous_version()) { GrowableArray<Method*>* methods = pv_node->prev_EMCP_methods(); if (methods == NULL) { // We have run into a PreviousVersion generation where // all methods were made obsolete during that generation's // RedefineClasses() operation. At the time of that // operation, all EMCP methods were flushed so we don't // have to go back any further. // // A NULL methods array is different than an empty methods // array. We cannot infer any optimizations about older // generations from an empty methods array for the current // generation. break; } for (int i = methods->length() - 1; i >= 0; i--) { Method* method = methods->at(i); // obsolete methods that are running are not deleted from // previous version array, but they are skipped here. if (!method->is_obsolete() && method->name() == m_name && method->signature() == m_signature) { RC_TRACE(0x00000800, ("%sing breakpoint in %s(%s)", meth_act == &Method::set_breakpoint ? "sett" : "clear", method->name()->as_C_string(), method->signature()->as_C_string())); (method->*meth_act)(_bci); break; } } } }
// Install the redefinition of a class -- // The original instanceKlass object (k_h) always represents the latest // version of the respective class. However, during class redefinition we swap // or replace much of its content with that of the instanceKlass object created // from the bytes of the redefine (k_h_new). Specifically, k_h points to the new // constantpool and methods objects, which we take from k_h_new. k_h_new, in turn, // assumes the role of the previous class version, with the old constantpool and // methods (taken from k_h) attached to it. k_h links to k_h_new to create a // linked list of class versions. void VM_RedefineClasses::redefine_single_class(jclass j_clazz, instanceKlassHandle k_h_new, TRAPS) { oop mirror = JNIHandles::resolve_non_null(j_clazz); klassOop k_oop = java_lang_Class::as_klassOop(mirror); instanceKlassHandle k_h = instanceKlassHandle(THREAD, k_oop); // Remove all breakpoints in methods of this class JvmtiBreakpoints& jvmti_breakpoints = JvmtiCurrentBreakpoints::get_jvmti_breakpoints(); jvmti_breakpoints.clearall_in_class_at_safepoint(k_oop); // Deoptimize all compiled code that depends on this class NOT_CORE(Universe::flush_evol_dependents_on(k_h)); _old_methods = k_h->methods(); _new_methods = k_h_new->methods(); _evolving_koop = k_oop; _old_constants = k_h->constants(); // flush the cached jmethodID fields for _old_methods flush_method_jmethod_id_cache(); // Patch the indexes into the constantpool from the array of fields of the evolving // class. This is required, because the layout of the new constantpool can be different, // so old indexes corresponding to field names and signatures can become invalid. patch_indexes_for_fields(k_h, k_h_new); // Make new constantpool object (and methodOops via it) point to the original class object k_h_new->constants()->set_pool_holder(k_h()); // Replace methods and constantpool k_h->set_methods(_new_methods); k_h_new->set_methods(_old_methods); // To prevent potential GCing of the old methods, // and to be able to undo operation easily. constantPoolOop old_constants = k_h->constants(); k_h->set_constants(k_h_new->constants()); k_h_new->set_constants(old_constants); // See the previous comment. check_methods_and_mark_as_old(); transfer_old_native_function_registrations(); // Replace inner_classes typeArrayOop old_inner_classes = k_h->inner_classes(); k_h->set_inner_classes(k_h_new->inner_classes()); k_h_new->set_inner_classes(old_inner_classes); // Initialize the vtable and interface table after // methods have been rewritten { ResourceMark rm(THREAD); k_h->vtable()->initialize_vtable(THREAD); // No exception can happen here k_h->itable()->initialize_itable(); } // Copy the "source file name" attribute from new class version k_h->set_source_file_name(k_h_new->source_file_name()); // Copy the "source debug extension" attribute from new class version k_h->set_source_debug_extension(k_h_new->source_debug_extension()); // Use of javac -g could be different in the old and the new if (k_h_new->access_flags().has_localvariable_table() != k_h->access_flags().has_localvariable_table()) { AccessFlags flags = k_h->access_flags(); if (k_h_new->access_flags().has_localvariable_table()) { flags.set_has_localvariable_table(); } else { flags.clear_has_localvariable_table(); } k_h->set_access_flags(flags); } // Replace class annotation fields values typeArrayOop old_class_annotations = k_h->class_annotations(); k_h->set_class_annotations(k_h_new->class_annotations()); k_h_new->set_class_annotations(old_class_annotations); // Replace fields annotation fields values objArrayOop old_fields_annotations = k_h->fields_annotations(); k_h->set_fields_annotations(k_h_new->fields_annotations()); k_h_new->set_fields_annotations(old_fields_annotations); // Replace methods annotation fields values objArrayOop old_methods_annotations = k_h->methods_annotations(); k_h->set_methods_annotations(k_h_new->methods_annotations()); k_h_new->set_methods_annotations(old_methods_annotations); // Replace methods parameter annotation fields values objArrayOop old_methods_parameter_annotations = k_h->methods_parameter_annotations(); k_h->set_methods_parameter_annotations(k_h_new->methods_parameter_annotations()); k_h_new->set_methods_parameter_annotations(old_methods_parameter_annotations); // Replace methods default annotation fields values objArrayOop old_methods_default_annotations = k_h->methods_default_annotations(); k_h->set_methods_default_annotations(k_h_new->methods_default_annotations()); k_h_new->set_methods_default_annotations(old_methods_default_annotations); // Replace major version number of class file u2 old_major_version = k_h->major_version(); k_h->set_major_version(k_h_new->major_version()); k_h_new->set_major_version(old_major_version); // Replace CP indexes for class and name+type of enclosing method u2 old_class_idx = k_h->enclosing_method_class_index(); u2 old_method_idx = k_h->enclosing_method_method_index(); k_h->set_enclosing_method_indices(k_h_new->enclosing_method_class_index(), k_h_new->enclosing_method_method_index()); k_h_new->set_enclosing_method_indices(old_class_idx, old_method_idx); // Maintain a linked list of versions of this class. // List is in ascending age order. Current version (k_h) is the head. if (k_h->has_previous_version()) { k_h_new->set_previous_version(k_h->previous_version()); } k_h->set_previous_version(k_h_new); // Adjust constantpool caches and vtables for all classes // that reference methods of the evolved class. SystemDictionary::classes_do(adjust_cpool_cache_and_vtable); k_h->set_rewritten_by_redefine(true); }
jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { // For consistency allocate memory using os::malloc wrapper. _k_h_new = (instanceKlassHandle *) os::malloc(sizeof(instanceKlassHandle) * _class_count); ResourceMark rm(THREAD); JvmtiThreadState *state = JavaThread::current()->jvmti_thread_state(); assert(state != NULL, "JvmtiThreadState not initialized"); for (int i = 0; i < _class_count; i++) { oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass); klassOop k_oop = java_lang_Class::as_klassOop(mirror); instanceKlassHandle k_h = instanceKlassHandle(THREAD, k_oop); symbolHandle k_name = symbolHandle(THREAD, k_h->name()); ClassFileStream st((u1*) _class_defs[i].class_bytes, _class_defs[i].class_byte_count, (char *)"__VM_RedefineClasses__"); // Parse the stream. Handle k_loader_h(THREAD, k_h->class_loader()); Handle protection_domain; // Set redefined class handle in JvmtiThreadState class. // This redefined class is sent to agent event handler for class file // load hook event. state->set_class_being_redefined(&k_h); klassOop k = SystemDictionary::parse_stream(k_name, k_loader_h, protection_domain, &st, THREAD); // Clear class_being_redefined just to be sure. state->set_class_being_redefined(NULL); instanceKlassHandle k_h_new (THREAD, k); if (HAS_PENDING_EXCEPTION) { symbolOop ex_name = PENDING_EXCEPTION->klass()->klass_part()->name(); CLEAR_PENDING_EXCEPTION; if (ex_name == vmSymbols::java_lang_UnsupportedClassVersionError()) { return JVMTI_ERROR_UNSUPPORTED_VERSION; } else if (ex_name == vmSymbols::java_lang_ClassFormatError()) { return JVMTI_ERROR_INVALID_CLASS_FORMAT; } else if (ex_name == vmSymbols::java_lang_ClassCircularityError()) { return JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION; } else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) { // The message will be "XXX (wrong name: YYY)" return JVMTI_ERROR_NAMES_DONT_MATCH; } else { // Just in case more exceptions can be thrown.. return JVMTI_ERROR_FAILS_VERIFICATION; } } // All its super classes should be linked to // initialize the vtable. instanceKlassHandle super(THREAD, k_h_new->super()); if (super.not_null() && !super->is_linked()) { super->link_class(THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return JVMTI_ERROR_INTERNAL; } } // See instanceKlass::link_klass_impl() { ObjectLocker ol(k_h_new, THREAD); Verifier::verify_byte_codes(k_h_new, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return JVMTI_ERROR_FAILS_VERIFICATION; } Rewriter::rewrite(k_h_new, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return JVMTI_ERROR_INTERNAL; } } jvmtiError res = compare_class_versions(k_h, k_h_new); if (res != JVMTI_ERROR_NONE) return res; _k_h_new[i] = k_h_new; } return JVMTI_ERROR_NONE; }
jvmdiError load_new_class_versions(TRAPS) { // For consistency allocate memory using os::malloc wrapper. _k_h_new = (instanceKlassHandle *) os::malloc(sizeof(instanceKlassHandle) * _class_count); ResourceMark rm(THREAD); for (int i = 0; i < _class_count; i++) { oop mirror = JNIHandles::resolve_non_null(_class_defs[i].clazz); klassOop k_oop = java_lang_Class::as_klassOop(mirror); instanceKlassHandle k_h = instanceKlassHandle(THREAD, k_oop); symbolHandle k_name = symbolHandle(THREAD, k_h->name()); ClassFileStream st((u1*) _class_defs[i].class_bytes, _class_defs[i].class_byte_count, NULL); // Parse the stream. Handle k_loader_h(THREAD, k_h->class_loader()); Handle protection_domain; klassOop k = SystemDictionary::parse_stream(k_name, k_loader_h, protection_domain, &st, THREAD); instanceKlassHandle k_h_new (THREAD, k); if (HAS_PENDING_EXCEPTION) { if (PENDING_EXCEPTION->klass()->klass_part()->name() == vmSymbols::java_lang_UnsupportedClassVersionError()) { CLEAR_PENDING_EXCEPTION; return JVMDI_ERROR_UNSUPPORTED_VERSION; } else if (PENDING_EXCEPTION->klass()->klass_part()->name() == vmSymbols::java_lang_ClassFormatError()) { CLEAR_PENDING_EXCEPTION; return JVMDI_ERROR_INVALID_CLASS_FORMAT; } else if (PENDING_EXCEPTION->klass()->klass_part()->name() == vmSymbols::java_lang_ClassCircularityError()) { CLEAR_PENDING_EXCEPTION; return JVMDI_ERROR_CIRCULAR_CLASS_DEFINITION; } else if (PENDING_EXCEPTION->klass()->klass_part()->name() == vmSymbols::java_lang_NoClassDefFoundError()) { // The message will be "XXX (wrong name: YYY)" CLEAR_PENDING_EXCEPTION; return JVMDI_ERROR_NAMES_DONT_MATCH; } else { // Just in case more exceptions can be thrown.. return JVMDI_ERROR_FAILS_VERIFICATION; } } // See instanceKlass::link_klass_impl() { ObjectLocker ol(k_h_new, THREAD); Verifier::verify_byte_codes(k_h_new, THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return JVMDI_ERROR_FAILS_VERIFICATION; } Rewriter::rewrite(k_h_new, THREAD); // No exception can happen here } jvmdiError res = compare_class_versions(k_h, k_h_new); if (res != JVMDI_ERROR_NONE) return res; _k_h_new[i] = k_h_new; } return JVMDI_ERROR_NONE; }