Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
corretto-11/src/src/hotspot/share/classfile/javaClasses.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
4586 lines (3873 sloc)
170 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. | |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
* | |
* This code is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License version 2 only, as | |
* published by the Free Software Foundation. | |
* | |
* This code is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
* version 2 for more details (a copy is included in the LICENSE file that | |
* accompanied this code). | |
* | |
* You should have received a copy of the GNU General Public License version | |
* 2 along with this work; if not, write to the Free Software Foundation, | |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
* | |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
* or visit www.oracle.com if you need additional information or have any | |
* questions. | |
* | |
*/ | |
#include "precompiled.hpp" | |
#include "classfile/altHashing.hpp" | |
#include "classfile/classLoaderData.inline.hpp" | |
#include "classfile/javaClasses.inline.hpp" | |
#include "classfile/moduleEntry.hpp" | |
#include "classfile/stringTable.hpp" | |
#include "classfile/vmSymbols.hpp" | |
#include "code/debugInfo.hpp" | |
#include "code/dependencyContext.hpp" | |
#include "code/pcDesc.hpp" | |
#include "interpreter/interpreter.hpp" | |
#include "interpreter/linkResolver.hpp" | |
#include "logging/log.hpp" | |
#include "logging/logStream.hpp" | |
#include "memory/oopFactory.hpp" | |
#include "memory/metaspaceShared.hpp" | |
#include "memory/resourceArea.hpp" | |
#include "memory/universe.hpp" | |
#include "oops/fieldStreams.hpp" | |
#include "oops/instanceKlass.hpp" | |
#include "oops/instanceMirrorKlass.hpp" | |
#include "oops/klass.hpp" | |
#include "oops/method.inline.hpp" | |
#include "oops/objArrayOop.inline.hpp" | |
#include "oops/oop.inline.hpp" | |
#include "oops/symbol.hpp" | |
#include "oops/typeArrayOop.inline.hpp" | |
#include "prims/resolvedMethodTable.hpp" | |
#include "runtime/fieldDescriptor.inline.hpp" | |
#include "runtime/frame.inline.hpp" | |
#include "runtime/handles.inline.hpp" | |
#include "runtime/interfaceSupport.inline.hpp" | |
#include "runtime/java.hpp" | |
#include "runtime/javaCalls.hpp" | |
#include "runtime/jniHandles.inline.hpp" | |
#include "runtime/safepoint.hpp" | |
#include "runtime/safepointVerifiers.hpp" | |
#include "runtime/thread.inline.hpp" | |
#include "runtime/vframe.inline.hpp" | |
#include "utilities/align.hpp" | |
#include "utilities/preserveException.hpp" | |
#if INCLUDE_JVMCI | |
#include "jvmci/jvmciJavaClasses.hpp" | |
#endif | |
#define INJECTED_FIELD_COMPUTE_OFFSET(klass, name, signature, may_be_java) \ | |
klass::_##name##_offset = JavaClasses::compute_injected_offset(JavaClasses::klass##_##name##_enum); | |
#if INCLUDE_CDS | |
#define INJECTED_FIELD_SERIALIZE_OFFSET(klass, name, signature, may_be_java) \ | |
f->do_u4((u4*)&_##name##_offset); | |
#endif | |
#define DECLARE_INJECTED_FIELD(klass, name, signature, may_be_java) \ | |
{ SystemDictionary::WK_KLASS_ENUM_NAME(klass), vmSymbols::VM_SYMBOL_ENUM_NAME(name##_name), vmSymbols::VM_SYMBOL_ENUM_NAME(signature), may_be_java }, | |
InjectedField JavaClasses::_injected_fields[] = { | |
ALL_INJECTED_FIELDS(DECLARE_INJECTED_FIELD) | |
}; | |
int JavaClasses::compute_injected_offset(InjectedFieldID id) { | |
return _injected_fields[id].compute_offset(); | |
} | |
InjectedField* JavaClasses::get_injected(Symbol* class_name, int* field_count) { | |
*field_count = 0; | |
vmSymbols::SID sid = vmSymbols::find_sid(class_name); | |
if (sid == vmSymbols::NO_SID) { | |
// Only well known classes can inject fields | |
return NULL; | |
} | |
int count = 0; | |
int start = -1; | |
#define LOOKUP_INJECTED_FIELD(klass, name, signature, may_be_java) \ | |
if (sid == vmSymbols::VM_SYMBOL_ENUM_NAME(klass)) { \ | |
count++; \ | |
if (start == -1) start = klass##_##name##_enum; \ | |
} | |
ALL_INJECTED_FIELDS(LOOKUP_INJECTED_FIELD); | |
#undef LOOKUP_INJECTED_FIELD | |
if (start != -1) { | |
*field_count = count; | |
return _injected_fields + start; | |
} | |
return NULL; | |
} | |
// Helpful routine for computing field offsets at run time rather than hardcoding them | |
// Finds local fields only, including static fields. Static field offsets are from the | |
// beginning of the mirror. | |
static void compute_offset(int &dest_offset, | |
InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol, | |
bool is_static = false) { | |
fieldDescriptor fd; | |
if (ik == NULL) { | |
ResourceMark rm; | |
log_error(class)("Mismatch JDK version for field: %s type: %s", name_symbol->as_C_string(), signature_symbol->as_C_string()); | |
vm_exit_during_initialization("Invalid layout of preloaded class"); | |
} | |
if (!ik->find_local_field(name_symbol, signature_symbol, &fd) || fd.is_static() != is_static) { | |
ResourceMark rm; | |
log_error(class)("Invalid layout of %s field: %s type: %s", ik->external_name(), | |
name_symbol->as_C_string(), signature_symbol->as_C_string()); | |
#ifndef PRODUCT | |
// Prints all fields and offsets | |
Log(class) lt; | |
LogStream ls(lt.error()); | |
ik->print_on(&ls); | |
#endif //PRODUCT | |
vm_exit_during_initialization("Invalid layout of preloaded class: use -Xlog:class+load=info to see the origin of the problem class"); | |
} | |
dest_offset = fd.offset(); | |
} | |
// Overloading to pass name as a string. | |
static void compute_offset(int& dest_offset, InstanceKlass* ik, | |
const char* name_string, Symbol* signature_symbol, | |
bool is_static = false) { | |
TempNewSymbol name = SymbolTable::probe(name_string, (int)strlen(name_string)); | |
if (name == NULL) { | |
ResourceMark rm; | |
log_error(class)("Name %s should be in the SymbolTable since its class is loaded", name_string); | |
vm_exit_during_initialization("Invalid layout of preloaded class", ik->external_name()); | |
} | |
compute_offset(dest_offset, ik, name, signature_symbol, is_static); | |
} | |
// Same as above but for "optional" offsets that might not be present in certain JDK versions | |
// Old versions should be cleaned out since Hotspot only supports the current JDK, and this | |
// function should be removed. | |
static void | |
compute_optional_offset(int& dest_offset, | |
InstanceKlass* ik, Symbol* name_symbol, Symbol* signature_symbol) { | |
fieldDescriptor fd; | |
if (ik->find_local_field(name_symbol, signature_symbol, &fd)) { | |
dest_offset = fd.offset(); | |
} | |
} | |
int java_lang_String::value_offset = 0; | |
int java_lang_String::hash_offset = 0; | |
int java_lang_String::coder_offset = 0; | |
bool java_lang_String::initialized = false; | |
bool java_lang_String::is_instance(oop obj) { | |
return is_instance_inlined(obj); | |
} | |
#if INCLUDE_CDS | |
#define FIELD_SERIALIZE_OFFSET(offset, klass, name, signature, is_static) \ | |
f->do_u4((u4*)&offset) | |
#define FIELD_SERIALIZE_OFFSET_OPTIONAL(offset, klass, name, signature) \ | |
f->do_u4((u4*)&offset) | |
#endif | |
#define FIELD_COMPUTE_OFFSET(offset, klass, name, signature, is_static) \ | |
compute_offset(offset, klass, name, vmSymbols::signature(), is_static) | |
#define FIELD_COMPUTE_OFFSET_OPTIONAL(offset, klass, name, signature) \ | |
compute_optional_offset(offset, klass, name, vmSymbols::signature()) | |
#define STRING_FIELDS_DO(macro) \ | |
macro(value_offset, k, vmSymbols::value_name(), byte_array_signature, false); \ | |
macro(hash_offset, k, "hash", int_signature, false); \ | |
macro(coder_offset, k, "coder", byte_signature, false) | |
void java_lang_String::compute_offsets() { | |
if (initialized) { | |
return; | |
} | |
InstanceKlass* k = SystemDictionary::String_klass(); | |
STRING_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
initialized = true; | |
} | |
#if INCLUDE_CDS | |
void java_lang_String::serialize_offsets(SerializeClosure* f) { | |
STRING_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
f->do_u4((u4*)&initialized); | |
} | |
#endif | |
class CompactStringsFixup : public FieldClosure { | |
private: | |
bool _value; | |
public: | |
CompactStringsFixup(bool value) : _value(value) {} | |
void do_field(fieldDescriptor* fd) { | |
if (fd->name() == vmSymbols::compact_strings_name()) { | |
oop mirror = fd->field_holder()->java_mirror(); | |
assert(fd->field_holder() == SystemDictionary::String_klass(), "Should be String"); | |
assert(mirror != NULL, "String must have mirror already"); | |
mirror->bool_field_put(fd->offset(), _value); | |
} | |
} | |
}; | |
void java_lang_String::set_compact_strings(bool value) { | |
CompactStringsFixup fix(value); | |
InstanceKlass::cast(SystemDictionary::String_klass())->do_local_static_fields(&fix); | |
} | |
Handle java_lang_String::basic_create(int length, bool is_latin1, TRAPS) { | |
assert(initialized, "Must be initialized"); | |
assert(CompactStrings || !is_latin1, "Must be UTF16 without CompactStrings"); | |
// Create the String object first, so there's a chance that the String | |
// and the char array it points to end up in the same cache line. | |
oop obj; | |
obj = SystemDictionary::String_klass()->allocate_instance(CHECK_NH); | |
// Create the char array. The String object must be handlized here | |
// because GC can happen as a result of the allocation attempt. | |
Handle h_obj(THREAD, obj); | |
int arr_length = is_latin1 ? length : length << 1; // 2 bytes per UTF16. | |
typeArrayOop buffer = oopFactory::new_byteArray(arr_length, CHECK_NH);; | |
// Point the String at the char array | |
obj = h_obj(); | |
set_value(obj, buffer); | |
// No need to zero the offset, allocation zero'ed the entire String object | |
set_coder(obj, is_latin1 ? CODER_LATIN1 : CODER_UTF16); | |
return h_obj; | |
} | |
Handle java_lang_String::create_from_unicode(jchar* unicode, int length, TRAPS) { | |
bool is_latin1 = CompactStrings && UNICODE::is_latin1(unicode, length); | |
Handle h_obj = basic_create(length, is_latin1, CHECK_NH); | |
typeArrayOop buffer = value(h_obj()); | |
assert(TypeArrayKlass::cast(buffer->klass())->element_type() == T_BYTE, "only byte[]"); | |
if (is_latin1) { | |
for (int index = 0; index < length; index++) { | |
buffer->byte_at_put(index, (jbyte)unicode[index]); | |
} | |
} else { | |
for (int index = 0; index < length; index++) { | |
buffer->char_at_put(index, unicode[index]); | |
} | |
} | |
#ifdef ASSERT | |
{ | |
ResourceMark rm; | |
char* expected = UNICODE::as_utf8(unicode, length); | |
char* actual = as_utf8_string(h_obj()); | |
if (strcmp(expected, actual) != 0) { | |
tty->print_cr("Unicode conversion failure: %s --> %s", expected, actual); | |
ShouldNotReachHere(); | |
} | |
} | |
#endif | |
return h_obj; | |
} | |
oop java_lang_String::create_oop_from_unicode(jchar* unicode, int length, TRAPS) { | |
Handle h_obj = create_from_unicode(unicode, length, CHECK_0); | |
return h_obj(); | |
} | |
Handle java_lang_String::create_from_str(const char* utf8_str, TRAPS) { | |
if (utf8_str == NULL) { | |
return Handle(); | |
} | |
bool has_multibyte, is_latin1; | |
int length = UTF8::unicode_length(utf8_str, is_latin1, has_multibyte); | |
if (!CompactStrings) { | |
has_multibyte = true; | |
is_latin1 = false; | |
} | |
Handle h_obj = basic_create(length, is_latin1, CHECK_NH); | |
if (length > 0) { | |
if (!has_multibyte) { | |
const jbyte* src = reinterpret_cast<const jbyte*>(utf8_str); | |
ArrayAccess<>::arraycopy_from_native(src, value(h_obj()), typeArrayOopDesc::element_offset<jbyte>(0), length); | |
} else if (is_latin1) { | |
UTF8::convert_to_unicode(utf8_str, value(h_obj())->byte_at_addr(0), length); | |
} else { | |
UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); | |
} | |
} | |
#ifdef ASSERT | |
// This check is too strict because the input string is not necessarily valid UTF8. | |
// For example, it may be created with arbitrary content via jni_NewStringUTF. | |
/* | |
{ | |
ResourceMark rm; | |
const char* expected = utf8_str; | |
char* actual = as_utf8_string(h_obj()); | |
if (strcmp(expected, actual) != 0) { | |
tty->print_cr("String conversion failure: %s --> %s", expected, actual); | |
ShouldNotReachHere(); | |
} | |
} | |
*/ | |
#endif | |
return h_obj; | |
} | |
oop java_lang_String::create_oop_from_str(const char* utf8_str, TRAPS) { | |
Handle h_obj = create_from_str(utf8_str, CHECK_0); | |
return h_obj(); | |
} | |
Handle java_lang_String::create_from_symbol(Symbol* symbol, TRAPS) { | |
const char* utf8_str = (char*)symbol->bytes(); | |
int utf8_len = symbol->utf8_length(); | |
bool has_multibyte, is_latin1; | |
int length = UTF8::unicode_length(utf8_str, utf8_len, is_latin1, has_multibyte); | |
if (!CompactStrings) { | |
has_multibyte = true; | |
is_latin1 = false; | |
} | |
Handle h_obj = basic_create(length, is_latin1, CHECK_NH); | |
if (length > 0) { | |
if (!has_multibyte) { | |
const jbyte* src = reinterpret_cast<const jbyte*>(utf8_str); | |
ArrayAccess<>::arraycopy_from_native(src, value(h_obj()), typeArrayOopDesc::element_offset<jbyte>(0), length); | |
} else if (is_latin1) { | |
UTF8::convert_to_unicode(utf8_str, value(h_obj())->byte_at_addr(0), length); | |
} else { | |
UTF8::convert_to_unicode(utf8_str, value(h_obj())->char_at_addr(0), length); | |
} | |
} | |
#ifdef ASSERT | |
{ | |
ResourceMark rm; | |
const char* expected = symbol->as_utf8(); | |
char* actual = as_utf8_string(h_obj()); | |
if (strncmp(expected, actual, utf8_len) != 0) { | |
tty->print_cr("Symbol conversion failure: %s --> %s", expected, actual); | |
ShouldNotReachHere(); | |
} | |
} | |
#endif | |
return h_obj; | |
} | |
// Converts a C string to a Java String based on current encoding | |
Handle java_lang_String::create_from_platform_dependent_str(const char* str, TRAPS) { | |
assert(str != NULL, "bad arguments"); | |
typedef jstring (*to_java_string_fn_t)(JNIEnv*, const char *); | |
static to_java_string_fn_t _to_java_string_fn = NULL; | |
if (_to_java_string_fn == NULL) { | |
void *lib_handle = os::native_java_library(); | |
_to_java_string_fn = CAST_TO_FN_PTR(to_java_string_fn_t, os::dll_lookup(lib_handle, "NewStringPlatform")); | |
if (_to_java_string_fn == NULL) { | |
fatal("NewStringPlatform missing"); | |
} | |
} | |
jstring js = NULL; | |
{ JavaThread* thread = (JavaThread*)THREAD; | |
assert(thread->is_Java_thread(), "must be java thread"); | |
HandleMark hm(thread); | |
ThreadToNativeFromVM ttn(thread); | |
js = (_to_java_string_fn)(thread->jni_environment(), str); | |
} | |
return Handle(THREAD, JNIHandles::resolve(js)); | |
} | |
// Converts a Java String to a native C string that can be used for | |
// native OS calls. | |
char* java_lang_String::as_platform_dependent_str(Handle java_string, TRAPS) { | |
typedef char* (*to_platform_string_fn_t)(JNIEnv*, jstring, bool*); | |
static to_platform_string_fn_t _to_platform_string_fn = NULL; | |
if (_to_platform_string_fn == NULL) { | |
void *lib_handle = os::native_java_library(); | |
_to_platform_string_fn = CAST_TO_FN_PTR(to_platform_string_fn_t, os::dll_lookup(lib_handle, "GetStringPlatformChars")); | |
if (_to_platform_string_fn == NULL) { | |
fatal("GetStringPlatformChars missing"); | |
} | |
} | |
char *native_platform_string; | |
{ JavaThread* thread = (JavaThread*)THREAD; | |
assert(thread->is_Java_thread(), "must be java thread"); | |
JNIEnv *env = thread->jni_environment(); | |
jstring js = (jstring) JNIHandles::make_local(env, java_string()); | |
bool is_copy; | |
HandleMark hm(thread); | |
ThreadToNativeFromVM ttn(thread); | |
native_platform_string = (_to_platform_string_fn)(env, js, &is_copy); | |
assert(is_copy == JNI_TRUE, "is_copy value changed"); | |
JNIHandles::destroy_local(js); | |
} | |
return native_platform_string; | |
} | |
Handle java_lang_String::char_converter(Handle java_string, jchar from_char, jchar to_char, TRAPS) { | |
oop obj = java_string(); | |
// Typical usage is to convert all '/' to '.' in string. | |
typeArrayOop value = java_lang_String::value(obj); | |
int length = java_lang_String::length(obj); | |
bool is_latin1 = java_lang_String::is_latin1(obj); | |
// First check if any from_char exist | |
int index; // Declared outside, used later | |
for (index = 0; index < length; index++) { | |
jchar c = !is_latin1 ? value->char_at(index) : | |
((jchar) value->byte_at(index)) & 0xff; | |
if (c == from_char) { | |
break; | |
} | |
} | |
if (index == length) { | |
// No from_char, so do not copy. | |
return java_string; | |
} | |
// Check if result string will be latin1 | |
bool to_is_latin1 = false; | |
// Replacement char must be latin1 | |
if (CompactStrings && UNICODE::is_latin1(to_char)) { | |
if (is_latin1) { | |
// Source string is latin1 as well | |
to_is_latin1 = true; | |
} else if (!UNICODE::is_latin1(from_char)) { | |
// We are replacing an UTF16 char. Scan string to | |
// check if result can be latin1 encoded. | |
to_is_latin1 = true; | |
for (index = 0; index < length; index++) { | |
jchar c = value->char_at(index); | |
if (c != from_char && !UNICODE::is_latin1(c)) { | |
to_is_latin1 = false; | |
break; | |
} | |
} | |
} | |
} | |
// Create new UNICODE (or byte) buffer. Must handlize value because GC | |
// may happen during String and char array creation. | |
typeArrayHandle h_value(THREAD, value); | |
Handle string = basic_create(length, to_is_latin1, CHECK_NH); | |
typeArrayOop from_buffer = h_value(); | |
typeArrayOop to_buffer = java_lang_String::value(string()); | |
// Copy contents | |
for (index = 0; index < length; index++) { | |
jchar c = (!is_latin1) ? from_buffer->char_at(index) : | |
((jchar) from_buffer->byte_at(index)) & 0xff; | |
if (c == from_char) { | |
c = to_char; | |
} | |
if (!to_is_latin1) { | |
to_buffer->char_at_put(index, c); | |
} else { | |
to_buffer->byte_at_put(index, (jbyte) c); | |
} | |
} | |
return string; | |
} | |
jchar* java_lang_String::as_unicode_string(oop java_string, int& length, TRAPS) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
jchar* result = NEW_RESOURCE_ARRAY_RETURN_NULL(jchar, length); | |
if (result != NULL) { | |
if (!is_latin1) { | |
for (int index = 0; index < length; index++) { | |
result[index] = value->char_at(index); | |
} | |
} else { | |
for (int index = 0; index < length; index++) { | |
result[index] = ((jchar) value->byte_at(index)) & 0xff; | |
} | |
} | |
} else { | |
THROW_MSG_0(vmSymbols::java_lang_OutOfMemoryError(), "could not allocate Unicode string"); | |
} | |
return result; | |
} | |
unsigned int java_lang_String::hash_code(oop java_string) { | |
int length = java_lang_String::length(java_string); | |
// Zero length string will hash to zero with String.hashCode() function. | |
if (length == 0) return 0; | |
typeArrayOop value = java_lang_String::value(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (is_latin1) { | |
return java_lang_String::hash_code(value->byte_at_addr(0), length); | |
} else { | |
return java_lang_String::hash_code(value->char_at_addr(0), length); | |
} | |
} | |
char* java_lang_String::as_quoted_ascii(oop java_string) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (length == 0) return NULL; | |
char* result; | |
int result_length; | |
if (!is_latin1) { | |
jchar* base = value->char_at_addr(0); | |
result_length = UNICODE::quoted_ascii_length(base, length) + 1; | |
result = NEW_RESOURCE_ARRAY(char, result_length); | |
UNICODE::as_quoted_ascii(base, length, result, result_length); | |
} else { | |
jbyte* base = value->byte_at_addr(0); | |
result_length = UNICODE::quoted_ascii_length(base, length) + 1; | |
result = NEW_RESOURCE_ARRAY(char, result_length); | |
UNICODE::as_quoted_ascii(base, length, result, result_length); | |
} | |
assert(result_length >= length + 1, "must not be shorter"); | |
assert(result_length == (int)strlen(result) + 1, "must match"); | |
return result; | |
} | |
Symbol* java_lang_String::as_symbol(oop java_string, TRAPS) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* base = (length == 0) ? NULL : value->char_at_addr(0); | |
Symbol* sym = SymbolTable::lookup_unicode(base, length, THREAD); | |
return sym; | |
} else { | |
ResourceMark rm; | |
jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); | |
const char* base = UNICODE::as_utf8(position, length); | |
Symbol* sym = SymbolTable::lookup(base, length, THREAD); | |
return sym; | |
} | |
} | |
Symbol* java_lang_String::as_symbol_or_null(oop java_string) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* base = (length == 0) ? NULL : value->char_at_addr(0); | |
return SymbolTable::probe_unicode(base, length); | |
} else { | |
ResourceMark rm; | |
jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); | |
const char* base = UNICODE::as_utf8(position, length); | |
return SymbolTable::probe(base, length); | |
} | |
} | |
int java_lang_String::utf8_length(oop java_string) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (length == 0) { | |
return 0; | |
} | |
if (!is_latin1) { | |
return UNICODE::utf8_length(value->char_at_addr(0), length); | |
} else { | |
return UNICODE::utf8_length(value->byte_at_addr(0), length); | |
} | |
} | |
char* java_lang_String::as_utf8_string(oop java_string) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* position = (length == 0) ? NULL : value->char_at_addr(0); | |
return UNICODE::as_utf8(position, length); | |
} else { | |
jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); | |
return UNICODE::as_utf8(position, length); | |
} | |
} | |
char* java_lang_String::as_utf8_string(oop java_string, char* buf, int buflen) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* position = (length == 0) ? NULL : value->char_at_addr(0); | |
return UNICODE::as_utf8(position, length, buf, buflen); | |
} else { | |
jbyte* position = (length == 0) ? NULL : value->byte_at_addr(0); | |
return UNICODE::as_utf8(position, length, buf, buflen); | |
} | |
} | |
char* java_lang_String::as_utf8_string(oop java_string, int start, int len) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
assert(start + len <= length, "just checking"); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* position = value->char_at_addr(start); | |
return UNICODE::as_utf8(position, len); | |
} else { | |
jbyte* position = value->byte_at_addr(start); | |
return UNICODE::as_utf8(position, len); | |
} | |
} | |
char* java_lang_String::as_utf8_string(oop java_string, int start, int len, char* buf, int buflen) { | |
typeArrayOop value = java_lang_String::value(java_string); | |
int length = java_lang_String::length(java_string); | |
assert(start + len <= length, "just checking"); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
jchar* position = value->char_at_addr(start); | |
return UNICODE::as_utf8(position, len, buf, buflen); | |
} else { | |
jbyte* position = value->byte_at_addr(start); | |
return UNICODE::as_utf8(position, len, buf, buflen); | |
} | |
} | |
bool java_lang_String::equals(oop java_string, jchar* chars, int len) { | |
assert(java_string->klass() == SystemDictionary::String_klass(), | |
"must be java_string"); | |
typeArrayOop value = java_lang_String::value_no_keepalive(java_string); | |
int length = java_lang_String::length(java_string); | |
if (length != len) { | |
return false; | |
} | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
if (!is_latin1) { | |
for (int i = 0; i < len; i++) { | |
if (value->char_at(i) != chars[i]) { | |
return false; | |
} | |
} | |
} else { | |
for (int i = 0; i < len; i++) { | |
if ((((jchar) value->byte_at(i)) & 0xff) != chars[i]) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
bool java_lang_String::equals(oop str1, oop str2) { | |
assert(str1->klass() == SystemDictionary::String_klass(), | |
"must be java String"); | |
assert(str2->klass() == SystemDictionary::String_klass(), | |
"must be java String"); | |
typeArrayOop value1 = java_lang_String::value_no_keepalive(str1); | |
int length1 = java_lang_String::length(str1); | |
bool is_latin1 = java_lang_String::is_latin1(str1); | |
typeArrayOop value2 = java_lang_String::value_no_keepalive(str2); | |
int length2 = java_lang_String::length(str2); | |
bool is_latin2 = java_lang_String::is_latin1(str2); | |
if ((length1 != length2) || (is_latin1 != is_latin2)) { | |
// Strings of different size or with different | |
// coders are never equal. | |
return false; | |
} | |
int blength1 = value1->length(); | |
for (int i = 0; i < blength1; i++) { | |
if (value1->byte_at(i) != value2->byte_at(i)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
void java_lang_String::print(oop java_string, outputStream* st) { | |
assert(java_string->klass() == SystemDictionary::String_klass(), "must be java_string"); | |
typeArrayOop value = java_lang_String::value_no_keepalive(java_string); | |
if (value == NULL) { | |
// This can happen if, e.g., printing a String | |
// object before its initializer has been called | |
st->print("NULL"); | |
return; | |
} | |
int length = java_lang_String::length(java_string); | |
bool is_latin1 = java_lang_String::is_latin1(java_string); | |
st->print("\""); | |
for (int index = 0; index < length; index++) { | |
st->print("%c", (!is_latin1) ? value->char_at(index) : | |
((jchar) value->byte_at(index)) & 0xff ); | |
} | |
st->print("\""); | |
} | |
static void initialize_static_field(fieldDescriptor* fd, Handle mirror, TRAPS) { | |
assert(mirror.not_null() && fd->is_static(), "just checking"); | |
if (fd->has_initial_value()) { | |
BasicType t = fd->field_type(); | |
switch (t) { | |
case T_BYTE: | |
mirror()->byte_field_put(fd->offset(), fd->int_initial_value()); | |
break; | |
case T_BOOLEAN: | |
mirror()->bool_field_put(fd->offset(), fd->int_initial_value()); | |
break; | |
case T_CHAR: | |
mirror()->char_field_put(fd->offset(), fd->int_initial_value()); | |
break; | |
case T_SHORT: | |
mirror()->short_field_put(fd->offset(), fd->int_initial_value()); | |
break; | |
case T_INT: | |
mirror()->int_field_put(fd->offset(), fd->int_initial_value()); | |
break; | |
case T_FLOAT: | |
mirror()->float_field_put(fd->offset(), fd->float_initial_value()); | |
break; | |
case T_DOUBLE: | |
mirror()->double_field_put(fd->offset(), fd->double_initial_value()); | |
break; | |
case T_LONG: | |
mirror()->long_field_put(fd->offset(), fd->long_initial_value()); | |
break; | |
case T_OBJECT: | |
{ | |
assert(fd->signature() == vmSymbols::string_signature(), | |
"just checking"); | |
if (DumpSharedSpaces && MetaspaceShared::is_archive_object(mirror())) { | |
// Archive the String field and update the pointer. | |
oop s = mirror()->obj_field(fd->offset()); | |
oop archived_s = StringTable::create_archived_string(s, CHECK); | |
mirror()->obj_field_put(fd->offset(), archived_s); | |
} else { | |
oop string = fd->string_initial_value(CHECK); | |
mirror()->obj_field_put(fd->offset(), string); | |
} | |
} | |
break; | |
default: | |
THROW_MSG(vmSymbols::java_lang_ClassFormatError(), | |
"Illegal ConstantValue attribute in class file"); | |
} | |
} | |
} | |
void java_lang_Class::fixup_mirror(Klass* k, TRAPS) { | |
assert(InstanceMirrorKlass::offset_of_static_fields() != 0, "must have been computed already"); | |
// If the offset was read from the shared archive, it was fixed up already | |
if (!k->is_shared()) { | |
if (k->is_instance_klass()) { | |
// During bootstrap, java.lang.Class wasn't loaded so static field | |
// offsets were computed without the size added it. Go back and | |
// update all the static field offsets to included the size. | |
for (JavaFieldStream fs(InstanceKlass::cast(k)); !fs.done(); fs.next()) { | |
if (fs.access_flags().is_static()) { | |
int real_offset = fs.offset() + InstanceMirrorKlass::offset_of_static_fields(); | |
fs.set_offset(real_offset); | |
} | |
} | |
} | |
} | |
if (k->is_shared() && k->has_raw_archived_mirror()) { | |
if (MetaspaceShared::open_archive_heap_region_mapped()) { | |
bool present = restore_archived_mirror(k, Handle(), Handle(), Handle(), CHECK); | |
assert(present, "Missing archived mirror for %s", k->external_name()); | |
return; | |
} else { | |
k->set_java_mirror_handle(NULL); | |
k->clear_has_raw_archived_mirror(); | |
} | |
} | |
create_mirror(k, Handle(), Handle(), Handle(), CHECK); | |
} | |
void java_lang_Class::initialize_mirror_fields(Klass* k, | |
Handle mirror, | |
Handle protection_domain, | |
TRAPS) { | |
// Allocate a simple java object for a lock. | |
// This needs to be a java object because during class initialization | |
// it can be held across a java call. | |
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK); | |
set_init_lock(mirror(), r); | |
// Set protection domain also | |
set_protection_domain(mirror(), protection_domain()); | |
// Initialize static fields | |
InstanceKlass::cast(k)->do_local_static_fields(&initialize_static_field, mirror, CHECK); | |
} | |
// Set the java.lang.Module module field in the java_lang_Class mirror | |
void java_lang_Class::set_mirror_module_field(Klass* k, Handle mirror, Handle module, TRAPS) { | |
if (module.is_null()) { | |
// During startup, the module may be NULL only if java.base has not been defined yet. | |
// Put the class on the fixup_module_list to patch later when the java.lang.Module | |
// for java.base is known. | |
assert(!Universe::is_module_initialized(), "Incorrect java.lang.Module pre module system initialization"); | |
bool javabase_was_defined = false; | |
{ | |
MutexLocker m1(Module_lock, THREAD); | |
// Keep list of classes needing java.base module fixup | |
if (!ModuleEntryTable::javabase_defined()) { | |
assert(k->java_mirror() != NULL, "Class's mirror is null"); | |
k->class_loader_data()->inc_keep_alive(); | |
assert(fixup_module_field_list() != NULL, "fixup_module_field_list not initialized"); | |
fixup_module_field_list()->push(k); | |
} else { | |
javabase_was_defined = true; | |
} | |
} | |
// If java.base was already defined then patch this particular class with java.base. | |
if (javabase_was_defined) { | |
ModuleEntry *javabase_entry = ModuleEntryTable::javabase_moduleEntry(); | |
assert(javabase_entry != NULL && javabase_entry->module() != NULL, | |
"Setting class module field, " JAVA_BASE_NAME " should be defined"); | |
Handle javabase_handle(THREAD, javabase_entry->module()); | |
set_module(mirror(), javabase_handle()); | |
} | |
} else { | |
assert(Universe::is_module_initialized() || | |
(ModuleEntryTable::javabase_defined() && | |
(oopDesc::equals(module(), ModuleEntryTable::javabase_moduleEntry()->module()))), | |
"Incorrect java.lang.Module specification while creating mirror"); | |
set_module(mirror(), module()); | |
} | |
} | |
// Statically allocate fixup lists because they always get created. | |
void java_lang_Class::allocate_fixup_lists() { | |
GrowableArray<Klass*>* mirror_list = | |
new (ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(40, true); | |
set_fixup_mirror_list(mirror_list); | |
GrowableArray<Klass*>* module_list = | |
new (ResourceObj::C_HEAP, mtModule) GrowableArray<Klass*>(500, true); | |
set_fixup_module_field_list(module_list); | |
} | |
void java_lang_Class::create_mirror(Klass* k, Handle class_loader, | |
Handle module, Handle protection_domain, TRAPS) { | |
assert(k != NULL, "Use create_basic_type_mirror for primitive types"); | |
assert(k->java_mirror() == NULL, "should only assign mirror once"); | |
// Use this moment of initialization to cache modifier_flags also, | |
// to support Class.getModifiers(). Instance classes recalculate | |
// the cached flags after the class file is parsed, but before the | |
// class is put into the system dictionary. | |
int computed_modifiers = k->compute_modifier_flags(CHECK); | |
k->set_modifier_flags(computed_modifiers); | |
// Class_klass has to be loaded because it is used to allocate | |
// the mirror. | |
if (SystemDictionary::Class_klass_loaded()) { | |
// Allocate mirror (java.lang.Class instance) | |
oop mirror_oop = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK); | |
Handle mirror(THREAD, mirror_oop); | |
Handle comp_mirror; | |
// Setup indirection from mirror->klass | |
java_lang_Class::set_klass(mirror(), k); | |
InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); | |
assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); | |
java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror())); | |
// It might also have a component mirror. This mirror must already exist. | |
if (k->is_array_klass()) { | |
if (k->is_typeArray_klass()) { | |
BasicType type = TypeArrayKlass::cast(k)->element_type(); | |
comp_mirror = Handle(THREAD, Universe::java_mirror(type)); | |
} else { | |
assert(k->is_objArray_klass(), "Must be"); | |
Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); | |
assert(element_klass != NULL, "Must have an element klass"); | |
comp_mirror = Handle(THREAD, element_klass->java_mirror()); | |
} | |
assert(comp_mirror() != NULL, "must have a mirror"); | |
// Two-way link between the array klass and its component mirror: | |
// (array_klass) k -> mirror -> component_mirror -> array_klass -> k | |
set_component_mirror(mirror(), comp_mirror()); | |
// See below for ordering dependencies between field array_klass in component mirror | |
// and java_mirror in this klass. | |
} else { | |
assert(k->is_instance_klass(), "Must be"); | |
initialize_mirror_fields(k, mirror, protection_domain, THREAD); | |
if (HAS_PENDING_EXCEPTION) { | |
// If any of the fields throws an exception like OOM remove the klass field | |
// from the mirror so GC doesn't follow it after the klass has been deallocated. | |
// This mirror looks like a primitive type, which logically it is because it | |
// it represents no class. | |
java_lang_Class::set_klass(mirror(), NULL); | |
return; | |
} | |
} | |
// set the classLoader field in the java_lang_Class instance | |
assert(oopDesc::equals(class_loader(), k->class_loader()), "should be same"); | |
set_class_loader(mirror(), class_loader()); | |
// Setup indirection from klass->mirror | |
// after any exceptions can happen during allocations. | |
k->set_java_mirror(mirror); | |
// Set the module field in the java_lang_Class instance. This must be done | |
// after the mirror is set. | |
set_mirror_module_field(k, mirror, module, THREAD); | |
if (comp_mirror() != NULL) { | |
// Set after k->java_mirror() is published, because compiled code running | |
// concurrently doesn't expect a k to have a null java_mirror. | |
release_set_array_klass(comp_mirror(), k); | |
} | |
} else { | |
assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized"); | |
fixup_mirror_list()->push(k); | |
} | |
} | |
#if INCLUDE_CDS_JAVA_HEAP | |
// Clears mirror fields. Static final fields with initial values are reloaded | |
// from constant pool. The object identity hash is in the object header and is | |
// not affected. | |
class ResetMirrorField: public FieldClosure { | |
private: | |
Handle _m; | |
public: | |
ResetMirrorField(Handle mirror) : _m(mirror) {} | |
void do_field(fieldDescriptor* fd) { | |
assert(DumpSharedSpaces, "dump time only"); | |
assert(_m.not_null(), "Mirror cannot be NULL"); | |
if (fd->is_static() && fd->has_initial_value()) { | |
initialize_static_field(fd, _m, Thread::current()); | |
return; | |
} | |
BasicType ft = fd->field_type(); | |
switch (ft) { | |
case T_BYTE: | |
_m()->byte_field_put(fd->offset(), 0); | |
break; | |
case T_CHAR: | |
_m()->char_field_put(fd->offset(), 0); | |
break; | |
case T_DOUBLE: | |
_m()->double_field_put(fd->offset(), 0); | |
break; | |
case T_FLOAT: | |
_m()->float_field_put(fd->offset(), 0); | |
break; | |
case T_INT: | |
_m()->int_field_put(fd->offset(), 0); | |
break; | |
case T_LONG: | |
_m()->long_field_put(fd->offset(), 0); | |
break; | |
case T_SHORT: | |
_m()->short_field_put(fd->offset(), 0); | |
break; | |
case T_BOOLEAN: | |
_m()->bool_field_put(fd->offset(), false); | |
break; | |
case T_ARRAY: | |
case T_OBJECT: { | |
// It might be useful to cache the String field, but | |
// for now just clear out any reference field | |
oop o = _m()->obj_field(fd->offset()); | |
_m()->obj_field_put(fd->offset(), NULL); | |
break; | |
} | |
default: | |
ShouldNotReachHere(); | |
break; | |
} | |
} | |
}; | |
void java_lang_Class::archive_basic_type_mirrors(TRAPS) { | |
assert(MetaspaceShared::is_heap_object_archiving_allowed(), | |
"MetaspaceShared::is_heap_object_archiving_allowed() must be true"); | |
for (int t = 0; t <= T_VOID; t++) { | |
oop m = Universe::_mirrors[t]; | |
if (m != NULL) { | |
// Update the field at _array_klass_offset to point to the relocated array klass. | |
oop archived_m = MetaspaceShared::archive_heap_object(m, THREAD); | |
Klass *ak = (Klass*)(archived_m->metadata_field(_array_klass_offset)); | |
assert(ak != NULL || t == T_VOID, "should not be NULL"); | |
if (ak != NULL) { | |
Klass *reloc_ak = MetaspaceShared::get_relocated_klass(ak); | |
archived_m->metadata_field_put(_array_klass_offset, reloc_ak); | |
} | |
// Clear the fields. Just to be safe | |
Klass *k = m->klass(); | |
Handle archived_mirror_h(THREAD, archived_m); | |
ResetMirrorField reset(archived_mirror_h); | |
InstanceKlass::cast(k)->do_nonstatic_fields(&reset); | |
log_trace(cds, heap, mirror)( | |
"Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, | |
type2name((BasicType)t), p2i(Universe::_mirrors[t]), p2i(archived_m)); | |
Universe::_mirrors[t] = archived_m; | |
} | |
} | |
assert(Universe::_mirrors[T_INT] != NULL && | |
Universe::_mirrors[T_FLOAT] != NULL && | |
Universe::_mirrors[T_DOUBLE] != NULL && | |
Universe::_mirrors[T_BYTE] != NULL && | |
Universe::_mirrors[T_BOOLEAN] != NULL && | |
Universe::_mirrors[T_CHAR] != NULL && | |
Universe::_mirrors[T_LONG] != NULL && | |
Universe::_mirrors[T_SHORT] != NULL && | |
Universe::_mirrors[T_VOID] != NULL, "sanity"); | |
Universe::set_int_mirror(Universe::_mirrors[T_INT]); | |
Universe::set_float_mirror(Universe::_mirrors[T_FLOAT]); | |
Universe::set_double_mirror(Universe::_mirrors[T_DOUBLE]); | |
Universe::set_byte_mirror(Universe::_mirrors[T_BYTE]); | |
Universe::set_bool_mirror(Universe::_mirrors[T_BOOLEAN]); | |
Universe::set_char_mirror(Universe::_mirrors[T_CHAR]); | |
Universe::set_long_mirror(Universe::_mirrors[T_LONG]); | |
Universe::set_short_mirror(Universe::_mirrors[T_SHORT]); | |
Universe::set_void_mirror(Universe::_mirrors[T_VOID]); | |
} | |
// | |
// After the mirror object is successfully archived, the archived | |
// klass is set with _has_archived_raw_mirror flag. | |
// | |
// The _has_archived_raw_mirror flag is cleared at runtime when the | |
// archived mirror is restored. If archived java heap data cannot | |
// be used at runtime, new mirror object is created for the shared | |
// class. The _has_archived_raw_mirror is cleared also during the process. | |
oop java_lang_Class::archive_mirror(Klass* k, TRAPS) { | |
assert(MetaspaceShared::is_heap_object_archiving_allowed(), | |
"MetaspaceShared::is_heap_object_archiving_allowed() must be true"); | |
// Mirror is already archived | |
if (k->has_raw_archived_mirror()) { | |
assert(k->archived_java_mirror_raw() != NULL, "no archived mirror"); | |
return k->archived_java_mirror_raw(); | |
} | |
// No mirror | |
oop mirror = k->java_mirror(); | |
if (mirror == NULL) { | |
return NULL; | |
} | |
if (k->is_instance_klass()) { | |
InstanceKlass *ik = InstanceKlass::cast(k); | |
assert(ik->signers() == NULL && !k->has_signer_and_not_archived(), | |
"class with signer cannot be supported"); | |
if (!(ik->is_shared_boot_class() || ik->is_shared_platform_class() || | |
ik->is_shared_app_class())) { | |
// Archiving mirror for classes from non-builtin loaders is not | |
// supported. Clear the _java_mirror within the archived class. | |
k->set_java_mirror_handle(NULL); | |
return NULL; | |
} | |
} | |
// Now start archiving the mirror object | |
oop archived_mirror = MetaspaceShared::archive_heap_object(mirror, THREAD); | |
if (archived_mirror == NULL) { | |
return NULL; | |
} | |
archived_mirror = process_archived_mirror(k, mirror, archived_mirror, THREAD); | |
if (archived_mirror == NULL) { | |
return NULL; | |
} | |
k->set_archived_java_mirror_raw(archived_mirror); | |
k->set_has_raw_archived_mirror(); | |
ResourceMark rm; | |
log_trace(cds, heap, mirror)( | |
"Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, | |
k->external_name(), p2i(mirror), p2i(archived_mirror)); | |
return archived_mirror; | |
} | |
// The process is based on create_mirror(). | |
oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror, | |
oop archived_mirror, | |
Thread *THREAD) { | |
// Clear nonstatic fields in archived mirror. Some of the fields will be set | |
// to archived metadata and objects below. | |
Klass *c = archived_mirror->klass(); | |
Handle archived_mirror_h(THREAD, archived_mirror); | |
ResetMirrorField reset(archived_mirror_h); | |
InstanceKlass::cast(c)->do_nonstatic_fields(&reset); | |
if (k->is_array_klass()) { | |
oop archived_comp_mirror; | |
if (k->is_typeArray_klass()) { | |
// The primitive type mirrors are already archived. Get the archived mirror. | |
oop comp_mirror = java_lang_Class::component_mirror(mirror); | |
archived_comp_mirror = MetaspaceShared::find_archived_heap_object(comp_mirror); | |
assert(archived_comp_mirror != NULL, "Must be"); | |
} else { | |
assert(k->is_objArray_klass(), "Must be"); | |
Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); | |
assert(element_klass != NULL, "Must have an element klass"); | |
archived_comp_mirror = archive_mirror(element_klass, THREAD); | |
if (archived_comp_mirror == NULL) { | |
return NULL; | |
} | |
} | |
java_lang_Class::set_component_mirror(archived_mirror, archived_comp_mirror); | |
} else { | |
assert(k->is_instance_klass(), "Must be"); | |
// Reset local static fields in the mirror | |
InstanceKlass::cast(k)->do_local_static_fields(&reset); | |
java_lang_Class:set_init_lock(archived_mirror, NULL); | |
set_protection_domain(archived_mirror, NULL); | |
} | |
// clear class loader and mirror_module_field | |
set_class_loader(archived_mirror, NULL); | |
set_module(archived_mirror, NULL); | |
// The archived mirror's field at _klass_offset is still pointing to the original | |
// klass. Updated the field in the archived mirror to point to the relocated | |
// klass in the archive. | |
Klass *reloc_k = MetaspaceShared::get_relocated_klass(as_Klass(mirror)); | |
log_debug(cds, heap, mirror)( | |
"Relocate mirror metadata field at _klass_offset from " PTR_FORMAT " ==> " PTR_FORMAT, | |
p2i(as_Klass(mirror)), p2i(reloc_k)); | |
archived_mirror->metadata_field_put(_klass_offset, reloc_k); | |
// The field at _array_klass_offset is pointing to the original one dimension | |
// higher array klass if exists. Relocate the pointer. | |
Klass *arr = array_klass_acquire(mirror); | |
if (arr != NULL) { | |
Klass *reloc_arr = MetaspaceShared::get_relocated_klass(arr); | |
log_debug(cds, heap, mirror)( | |
"Relocate mirror metadata field at _array_klass_offset from " PTR_FORMAT " ==> " PTR_FORMAT, | |
p2i(arr), p2i(reloc_arr)); | |
archived_mirror->metadata_field_put(_array_klass_offset, reloc_arr); | |
} | |
return archived_mirror; | |
} | |
// Returns true if the mirror is updated, false if no archived mirror | |
// data is present. After the archived mirror object is restored, the | |
// shared klass' _has_raw_archived_mirror flag is cleared. | |
bool java_lang_Class::restore_archived_mirror(Klass *k, | |
Handle class_loader, Handle module, | |
Handle protection_domain, TRAPS) { | |
oop m = MetaspaceShared::materialize_archived_object(k->archived_java_mirror_raw()); | |
if (m == NULL) { | |
return false; | |
} | |
log_debug(cds, mirror)("Archived mirror is: " PTR_FORMAT, p2i(m)); | |
// mirror is archived, restore | |
assert(MetaspaceShared::is_archive_object(m), "must be archived mirror object"); | |
Handle mirror(THREAD, m); | |
// The java.lang.Class field offsets were archived and reloaded from archive. | |
// No need to put classes on the fixup_mirror_list before java.lang.Class | |
// is loaded. | |
if (!k->is_array_klass()) { | |
// - local static final fields with initial values were initialized at dump time | |
// create the init_lock | |
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_(false)); | |
set_init_lock(mirror(), r); | |
if (protection_domain.not_null()) { | |
set_protection_domain(mirror(), protection_domain()); | |
} | |
} | |
assert(class_loader() == k->class_loader(), "should be same"); | |
if (class_loader.not_null()) { | |
set_class_loader(mirror(), class_loader()); | |
} | |
k->set_java_mirror(mirror); | |
k->clear_has_raw_archived_mirror(); | |
set_mirror_module_field(k, mirror, module, THREAD); | |
ResourceMark rm; | |
log_trace(cds, heap, mirror)( | |
"Restored %s archived mirror " PTR_FORMAT, k->external_name(), p2i(mirror())); | |
return true; | |
} | |
#endif // INCLUDE_CDS_JAVA_HEAP | |
void java_lang_Class::fixup_module_field(Klass* k, Handle module) { | |
assert(_module_offset != 0, "must have been computed already"); | |
java_lang_Class::set_module(k->java_mirror(), module()); | |
} | |
int java_lang_Class::oop_size(oop java_class) { | |
assert(_oop_size_offset != 0, "must be set"); | |
int size = java_class->int_field(_oop_size_offset); | |
assert(size > 0, "Oop size must be greater than zero, not %d", size); | |
return size; | |
} | |
void java_lang_Class::set_oop_size(HeapWord* java_class, int size) { | |
assert(_oop_size_offset != 0, "must be set"); | |
assert(size > 0, "Oop size must be greater than zero, not %d", size); | |
*(int*)(((char*)java_class) + _oop_size_offset) = size; | |
} | |
int java_lang_Class::static_oop_field_count(oop java_class) { | |
assert(_static_oop_field_count_offset != 0, "must be set"); | |
return java_class->int_field(_static_oop_field_count_offset); | |
} | |
void java_lang_Class::set_static_oop_field_count(oop java_class, int size) { | |
assert(_static_oop_field_count_offset != 0, "must be set"); | |
java_class->int_field_put(_static_oop_field_count_offset, size); | |
} | |
oop java_lang_Class::protection_domain(oop java_class) { | |
assert(_protection_domain_offset != 0, "must be set"); | |
return java_class->obj_field(_protection_domain_offset); | |
} | |
void java_lang_Class::set_protection_domain(oop java_class, oop pd) { | |
assert(_protection_domain_offset != 0, "must be set"); | |
java_class->obj_field_put(_protection_domain_offset, pd); | |
} | |
void java_lang_Class::set_component_mirror(oop java_class, oop comp_mirror) { | |
assert(_component_mirror_offset != 0, "must be set"); | |
java_class->obj_field_put(_component_mirror_offset, comp_mirror); | |
} | |
oop java_lang_Class::component_mirror(oop java_class) { | |
assert(_component_mirror_offset != 0, "must be set"); | |
return java_class->obj_field(_component_mirror_offset); | |
} | |
oop java_lang_Class::init_lock(oop java_class) { | |
assert(_init_lock_offset != 0, "must be set"); | |
return java_class->obj_field(_init_lock_offset); | |
} | |
void java_lang_Class::set_init_lock(oop java_class, oop init_lock) { | |
assert(_init_lock_offset != 0, "must be set"); | |
java_class->obj_field_put(_init_lock_offset, init_lock); | |
} | |
objArrayOop java_lang_Class::signers(oop java_class) { | |
assert(_signers_offset != 0, "must be set"); | |
return (objArrayOop)java_class->obj_field(_signers_offset); | |
} | |
void java_lang_Class::set_signers(oop java_class, objArrayOop signers) { | |
assert(_signers_offset != 0, "must be set"); | |
java_class->obj_field_put(_signers_offset, (oop)signers); | |
} | |
void java_lang_Class::set_class_loader(oop java_class, oop loader) { | |
// jdk7 runs Queens in bootstrapping and jdk8-9 has no coordinated pushes yet. | |
if (_class_loader_offset != 0) { | |
java_class->obj_field_put(_class_loader_offset, loader); | |
} | |
} | |
oop java_lang_Class::class_loader(oop java_class) { | |
assert(_class_loader_offset != 0, "must be set"); | |
return java_class->obj_field(_class_loader_offset); | |
} | |
oop java_lang_Class::module(oop java_class) { | |
assert(_module_offset != 0, "must be set"); | |
return java_class->obj_field(_module_offset); | |
} | |
void java_lang_Class::set_module(oop java_class, oop module) { | |
assert(_module_offset != 0, "must be set"); | |
java_class->obj_field_put(_module_offset, module); | |
} | |
oop java_lang_Class::name(Handle java_class, TRAPS) { | |
assert(_name_offset != 0, "must be set"); | |
oop o = java_class->obj_field(_name_offset); | |
if (o == NULL) { | |
o = StringTable::intern(java_lang_Class::as_external_name(java_class()), THREAD); | |
java_class->obj_field_put(_name_offset, o); | |
} | |
return o; | |
} | |
oop java_lang_Class::source_file(oop java_class) { | |
assert(_source_file_offset != 0, "must be set"); | |
return java_class->obj_field(_source_file_offset); | |
} | |
void java_lang_Class::set_source_file(oop java_class, oop source_file) { | |
assert(_source_file_offset != 0, "must be set"); | |
java_class->obj_field_put(_source_file_offset, source_file); | |
} | |
oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) { | |
// This should be improved by adding a field at the Java level or by | |
// introducing a new VM klass (see comment in ClassFileParser) | |
oop java_class = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(NULL, CHECK_0); | |
if (type != T_VOID) { | |
Klass* aklass = Universe::typeArrayKlassObj(type); | |
assert(aklass != NULL, "correct bootstrap"); | |
release_set_array_klass(java_class, aklass); | |
} | |
#ifdef ASSERT | |
InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(SystemDictionary::Class_klass()); | |
assert(java_lang_Class::static_oop_field_count(java_class) == 0, "should have been zeroed by allocation"); | |
#endif | |
return java_class; | |
} | |
Klass* java_lang_Class::as_Klass(oop java_class) { | |
//%note memory_2 | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
Klass* k = ((Klass*)java_class->metadata_field(_klass_offset)); | |
assert(k == NULL || k->is_klass(), "type check"); | |
return k; | |
} | |
void java_lang_Class::set_klass(oop java_class, Klass* klass) { | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
java_class->metadata_field_put(_klass_offset, klass); | |
} | |
void java_lang_Class::print_signature(oop java_class, outputStream* st) { | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
Symbol* name = NULL; | |
bool is_instance = false; | |
if (is_primitive(java_class)) { | |
name = vmSymbols::type_signature(primitive_type(java_class)); | |
} else { | |
Klass* k = as_Klass(java_class); | |
is_instance = k->is_instance_klass(); | |
name = k->name(); | |
} | |
if (name == NULL) { | |
st->print("<null>"); | |
return; | |
} | |
if (is_instance) st->print("L"); | |
st->write((char*) name->base(), (int) name->utf8_length()); | |
if (is_instance) st->print(";"); | |
} | |
Symbol* java_lang_Class::as_signature(oop java_class, bool intern_if_not_found, TRAPS) { | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
Symbol* name; | |
if (is_primitive(java_class)) { | |
name = vmSymbols::type_signature(primitive_type(java_class)); | |
// Because this can create a new symbol, the caller has to decrement | |
// the refcount, so make adjustment here and below for symbols returned | |
// that are not created or incremented due to a successful lookup. | |
name->increment_refcount(); | |
} else { | |
Klass* k = as_Klass(java_class); | |
if (!k->is_instance_klass()) { | |
name = k->name(); | |
name->increment_refcount(); | |
} else { | |
ResourceMark rm; | |
const char* sigstr = k->signature_name(); | |
int siglen = (int) strlen(sigstr); | |
if (!intern_if_not_found) { | |
name = SymbolTable::probe(sigstr, siglen); | |
} else { | |
name = SymbolTable::new_symbol(sigstr, siglen, THREAD); | |
} | |
} | |
} | |
return name; | |
} | |
// Returns the Java name for this Java mirror (Resource allocated) | |
// See Klass::external_name(). | |
// For primitive type Java mirrors, its type name is returned. | |
const char* java_lang_Class::as_external_name(oop java_class) { | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
const char* name = NULL; | |
if (is_primitive(java_class)) { | |
name = type2name(primitive_type(java_class)); | |
} else { | |
name = as_Klass(java_class)->external_name(); | |
} | |
if (name == NULL) { | |
name = "<null>"; | |
} | |
return name; | |
} | |
Klass* java_lang_Class::array_klass_acquire(oop java_class) { | |
Klass* k = ((Klass*)java_class->metadata_field_acquire(_array_klass_offset)); | |
assert(k == NULL || k->is_klass() && k->is_array_klass(), "should be array klass"); | |
return k; | |
} | |
void java_lang_Class::release_set_array_klass(oop java_class, Klass* klass) { | |
assert(klass->is_klass() && klass->is_array_klass(), "should be array klass"); | |
java_class->release_metadata_field_put(_array_klass_offset, klass); | |
} | |
bool java_lang_Class::is_primitive(oop java_class) { | |
// should assert: | |
//assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
bool is_primitive = (java_class->metadata_field(_klass_offset) == NULL); | |
#ifdef ASSERT | |
if (is_primitive) { | |
Klass* k = ((Klass*)java_class->metadata_field(_array_klass_offset)); | |
assert(k == NULL || is_java_primitive(ArrayKlass::cast(k)->element_type()), | |
"Should be either the T_VOID primitive or a java primitive"); | |
} | |
#endif | |
return is_primitive; | |
} | |
BasicType java_lang_Class::primitive_type(oop java_class) { | |
assert(java_lang_Class::is_primitive(java_class), "just checking"); | |
Klass* ak = ((Klass*)java_class->metadata_field(_array_klass_offset)); | |
BasicType type = T_VOID; | |
if (ak != NULL) { | |
// Note: create_basic_type_mirror above initializes ak to a non-null value. | |
type = ArrayKlass::cast(ak)->element_type(); | |
} else { | |
assert(oopDesc::equals(java_class, Universe::void_mirror()), "only valid non-array primitive"); | |
} | |
assert(oopDesc::equals(Universe::java_mirror(type), java_class), "must be consistent"); | |
return type; | |
} | |
BasicType java_lang_Class::as_BasicType(oop java_class, Klass** reference_klass) { | |
assert(java_lang_Class::is_instance(java_class), "must be a Class object"); | |
if (is_primitive(java_class)) { | |
if (reference_klass != NULL) | |
(*reference_klass) = NULL; | |
return primitive_type(java_class); | |
} else { | |
if (reference_klass != NULL) | |
(*reference_klass) = as_Klass(java_class); | |
return T_OBJECT; | |
} | |
} | |
oop java_lang_Class::primitive_mirror(BasicType t) { | |
oop mirror = Universe::java_mirror(t); | |
assert(mirror != NULL && mirror->is_a(SystemDictionary::Class_klass()), "must be a Class"); | |
assert(java_lang_Class::is_primitive(mirror), "must be primitive"); | |
return mirror; | |
} | |
bool java_lang_Class::offsets_computed = false; | |
int java_lang_Class::classRedefinedCount_offset = -1; | |
#define CLASS_FIELDS_DO(macro) \ | |
macro(classRedefinedCount_offset, k, "classRedefinedCount", int_signature, false) ; \ | |
macro(_class_loader_offset, k, "classLoader", classloader_signature, false); \ | |
macro(_component_mirror_offset, k, "componentType", class_signature, false); \ | |
macro(_module_offset, k, "module", module_signature, false); \ | |
macro(_name_offset, k, "name", string_signature, false); \ | |
void java_lang_Class::compute_offsets() { | |
if (offsets_computed) { | |
return; | |
} | |
offsets_computed = true; | |
InstanceKlass* k = SystemDictionary::Class_klass(); | |
CLASS_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
// Init lock is a C union with component_mirror. Only instanceKlass mirrors have | |
// init_lock and only ArrayKlass mirrors have component_mirror. Since both are oops | |
// GC treats them the same. | |
_init_lock_offset = _component_mirror_offset; | |
CLASS_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_Class::serialize_offsets(SerializeClosure* f) { | |
f->do_u4((u4*)&offsets_computed); | |
f->do_u4((u4*)&_init_lock_offset); | |
CLASS_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
CLASS_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
int java_lang_Class::classRedefinedCount(oop the_class_mirror) { | |
if (classRedefinedCount_offset == -1) { | |
// If we don't have an offset for it then just return -1 as a marker. | |
return -1; | |
} | |
return the_class_mirror->int_field(classRedefinedCount_offset); | |
} | |
void java_lang_Class::set_classRedefinedCount(oop the_class_mirror, int value) { | |
if (classRedefinedCount_offset == -1) { | |
// If we don't have an offset for it then nothing to set. | |
return; | |
} | |
the_class_mirror->int_field_put(classRedefinedCount_offset, value); | |
} | |
// Note: JDK1.1 and before had a privateInfo_offset field which was used for the | |
// platform thread structure, and a eetop offset which was used for thread | |
// local storage (and unused by the HotSpot VM). In JDK1.2 the two structures | |
// merged, so in the HotSpot VM we just use the eetop field for the thread | |
// instead of the privateInfo_offset. | |
// | |
// Note: The stackSize field is only present starting in 1.4. | |
int java_lang_Thread::_name_offset = 0; | |
int java_lang_Thread::_group_offset = 0; | |
int java_lang_Thread::_contextClassLoader_offset = 0; | |
int java_lang_Thread::_inheritedAccessControlContext_offset = 0; | |
int java_lang_Thread::_priority_offset = 0; | |
int java_lang_Thread::_eetop_offset = 0; | |
int java_lang_Thread::_daemon_offset = 0; | |
int java_lang_Thread::_stillborn_offset = 0; | |
int java_lang_Thread::_stackSize_offset = 0; | |
int java_lang_Thread::_tid_offset = 0; | |
int java_lang_Thread::_thread_status_offset = 0; | |
int java_lang_Thread::_park_blocker_offset = 0; | |
int java_lang_Thread::_park_event_offset = 0 ; | |
#define THREAD_FIELDS_DO(macro) \ | |
macro(_name_offset, k, vmSymbols::name_name(), string_signature, false); \ | |
macro(_group_offset, k, vmSymbols::group_name(), threadgroup_signature, false); \ | |
macro(_contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), classloader_signature, false); \ | |
macro(_inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), accesscontrolcontext_signature, false); \ | |
macro(_priority_offset, k, vmSymbols::priority_name(), int_signature, false); \ | |
macro(_daemon_offset, k, vmSymbols::daemon_name(), bool_signature, false); \ | |
macro(_eetop_offset, k, "eetop", long_signature, false); \ | |
macro(_stillborn_offset, k, "stillborn", bool_signature, false); \ | |
macro(_stackSize_offset, k, "stackSize", long_signature, false); \ | |
macro(_tid_offset, k, "tid", long_signature, false); \ | |
macro(_thread_status_offset, k, "threadStatus", int_signature, false); \ | |
macro(_park_blocker_offset, k, "parkBlocker", object_signature, false); \ | |
macro(_park_event_offset, k, "nativeParkEventPointer", long_signature, false) | |
void java_lang_Thread::compute_offsets() { | |
assert(_group_offset == 0, "offsets should be initialized only once"); | |
InstanceKlass* k = SystemDictionary::Thread_klass(); | |
THREAD_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_Thread::serialize_offsets(SerializeClosure* f) { | |
THREAD_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
JavaThread* java_lang_Thread::thread(oop java_thread) { | |
return (JavaThread*)java_thread->address_field(_eetop_offset); | |
} | |
void java_lang_Thread::set_thread(oop java_thread, JavaThread* thread) { | |
java_thread->address_field_put(_eetop_offset, (address)thread); | |
} | |
oop java_lang_Thread::name(oop java_thread) { | |
return java_thread->obj_field(_name_offset); | |
} | |
void java_lang_Thread::set_name(oop java_thread, oop name) { | |
java_thread->obj_field_put(_name_offset, name); | |
} | |
ThreadPriority java_lang_Thread::priority(oop java_thread) { | |
return (ThreadPriority)java_thread->int_field(_priority_offset); | |
} | |
void java_lang_Thread::set_priority(oop java_thread, ThreadPriority priority) { | |
java_thread->int_field_put(_priority_offset, priority); | |
} | |
oop java_lang_Thread::threadGroup(oop java_thread) { | |
return java_thread->obj_field(_group_offset); | |
} | |
bool java_lang_Thread::is_stillborn(oop java_thread) { | |
return java_thread->bool_field(_stillborn_offset) != 0; | |
} | |
// We never have reason to turn the stillborn bit off | |
void java_lang_Thread::set_stillborn(oop java_thread) { | |
java_thread->bool_field_put(_stillborn_offset, true); | |
} | |
bool java_lang_Thread::is_alive(oop java_thread) { | |
JavaThread* thr = java_lang_Thread::thread(java_thread); | |
return (thr != NULL); | |
} | |
bool java_lang_Thread::is_daemon(oop java_thread) { | |
return java_thread->bool_field(_daemon_offset) != 0; | |
} | |
void java_lang_Thread::set_daemon(oop java_thread) { | |
java_thread->bool_field_put(_daemon_offset, true); | |
} | |
oop java_lang_Thread::context_class_loader(oop java_thread) { | |
return java_thread->obj_field(_contextClassLoader_offset); | |
} | |
oop java_lang_Thread::inherited_access_control_context(oop java_thread) { | |
return java_thread->obj_field(_inheritedAccessControlContext_offset); | |
} | |
jlong java_lang_Thread::stackSize(oop java_thread) { | |
if (_stackSize_offset > 0) { | |
return java_thread->long_field(_stackSize_offset); | |
} else { | |
return 0; | |
} | |
} | |
// Write the thread status value to threadStatus field in java.lang.Thread java class. | |
void java_lang_Thread::set_thread_status(oop java_thread, | |
java_lang_Thread::ThreadStatus status) { | |
// The threadStatus is only present starting in 1.5 | |
if (_thread_status_offset > 0) { | |
java_thread->int_field_put(_thread_status_offset, status); | |
} | |
} | |
// Read thread status value from threadStatus field in java.lang.Thread java class. | |
java_lang_Thread::ThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { | |
// Make sure the caller is operating on behalf of the VM or is | |
// running VM code (state == _thread_in_vm). | |
assert(Threads_lock->owned_by_self() || Thread::current()->is_VM_thread() || | |
JavaThread::current()->thread_state() == _thread_in_vm, | |
"Java Thread is not running in vm"); | |
// The threadStatus is only present starting in 1.5 | |
if (_thread_status_offset > 0) { | |
return (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); | |
} else { | |
// All we can easily figure out is if it is alive, but that is | |
// enough info for a valid unknown status. | |
// These aren't restricted to valid set ThreadStatus values, so | |
// use JVMTI values and cast. | |
JavaThread* thr = java_lang_Thread::thread(java_thread); | |
if (thr == NULL) { | |
// the thread hasn't run yet or is in the process of exiting | |
return NEW; | |
} | |
return (java_lang_Thread::ThreadStatus)JVMTI_THREAD_STATE_ALIVE; | |
} | |
} | |
jlong java_lang_Thread::thread_id(oop java_thread) { | |
// The thread ID field is only present starting in 1.5 | |
if (_tid_offset > 0) { | |
return java_thread->long_field(_tid_offset); | |
} else { | |
return 0; | |
} | |
} | |
oop java_lang_Thread::park_blocker(oop java_thread) { | |
assert(JDK_Version::current().supports_thread_park_blocker() && | |
_park_blocker_offset != 0, "Must support parkBlocker field"); | |
if (_park_blocker_offset > 0) { | |
return java_thread->obj_field(_park_blocker_offset); | |
} | |
return NULL; | |
} | |
jlong java_lang_Thread::park_event(oop java_thread) { | |
if (_park_event_offset > 0) { | |
return java_thread->long_field(_park_event_offset); | |
} | |
return 0; | |
} | |
bool java_lang_Thread::set_park_event(oop java_thread, jlong ptr) { | |
if (_park_event_offset > 0) { | |
java_thread->long_field_put(_park_event_offset, ptr); | |
return true; | |
} | |
return false; | |
} | |
const char* java_lang_Thread::thread_status_name(oop java_thread) { | |
assert(_thread_status_offset != 0, "Must have thread status"); | |
ThreadStatus status = (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); | |
switch (status) { | |
case NEW : return "NEW"; | |
case RUNNABLE : return "RUNNABLE"; | |
case SLEEPING : return "TIMED_WAITING (sleeping)"; | |
case IN_OBJECT_WAIT : return "WAITING (on object monitor)"; | |
case IN_OBJECT_WAIT_TIMED : return "TIMED_WAITING (on object monitor)"; | |
case PARKED : return "WAITING (parking)"; | |
case PARKED_TIMED : return "TIMED_WAITING (parking)"; | |
case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)"; | |
case TERMINATED : return "TERMINATED"; | |
default : return "UNKNOWN"; | |
}; | |
} | |
int java_lang_ThreadGroup::_parent_offset = 0; | |
int java_lang_ThreadGroup::_name_offset = 0; | |
int java_lang_ThreadGroup::_threads_offset = 0; | |
int java_lang_ThreadGroup::_groups_offset = 0; | |
int java_lang_ThreadGroup::_maxPriority_offset = 0; | |
int java_lang_ThreadGroup::_destroyed_offset = 0; | |
int java_lang_ThreadGroup::_daemon_offset = 0; | |
int java_lang_ThreadGroup::_nthreads_offset = 0; | |
int java_lang_ThreadGroup::_ngroups_offset = 0; | |
oop java_lang_ThreadGroup::parent(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return java_thread_group->obj_field(_parent_offset); | |
} | |
// ("name as oop" accessor is not necessary) | |
const char* java_lang_ThreadGroup::name(oop java_thread_group) { | |
oop name = java_thread_group->obj_field(_name_offset); | |
// ThreadGroup.name can be null | |
if (name != NULL) { | |
return java_lang_String::as_utf8_string(name); | |
} | |
return NULL; | |
} | |
int java_lang_ThreadGroup::nthreads(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return java_thread_group->int_field(_nthreads_offset); | |
} | |
objArrayOop java_lang_ThreadGroup::threads(oop java_thread_group) { | |
oop threads = java_thread_group->obj_field(_threads_offset); | |
assert(threads != NULL, "threadgroups should have threads"); | |
assert(threads->is_objArray(), "just checking"); // Todo: Add better type checking code | |
return objArrayOop(threads); | |
} | |
int java_lang_ThreadGroup::ngroups(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return java_thread_group->int_field(_ngroups_offset); | |
} | |
objArrayOop java_lang_ThreadGroup::groups(oop java_thread_group) { | |
oop groups = java_thread_group->obj_field(_groups_offset); | |
assert(groups == NULL || groups->is_objArray(), "just checking"); // Todo: Add better type checking code | |
return objArrayOop(groups); | |
} | |
ThreadPriority java_lang_ThreadGroup::maxPriority(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return (ThreadPriority) java_thread_group->int_field(_maxPriority_offset); | |
} | |
bool java_lang_ThreadGroup::is_destroyed(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return java_thread_group->bool_field(_destroyed_offset) != 0; | |
} | |
bool java_lang_ThreadGroup::is_daemon(oop java_thread_group) { | |
assert(oopDesc::is_oop(java_thread_group), "thread group must be oop"); | |
return java_thread_group->bool_field(_daemon_offset) != 0; | |
} | |
#define THREADGROUP_FIELDS_DO(macro) \ | |
macro(_parent_offset, k, vmSymbols::parent_name(), threadgroup_signature, false); \ | |
macro(_name_offset, k, vmSymbols::name_name(), string_signature, false); \ | |
macro(_threads_offset, k, vmSymbols::threads_name(), thread_array_signature, false); \ | |
macro(_groups_offset, k, vmSymbols::groups_name(), threadgroup_array_signature, false); \ | |
macro(_maxPriority_offset, k, vmSymbols::maxPriority_name(), int_signature, false); \ | |
macro(_destroyed_offset, k, vmSymbols::destroyed_name(), bool_signature, false); \ | |
macro(_daemon_offset, k, vmSymbols::daemon_name(), bool_signature, false); \ | |
macro(_nthreads_offset, k, vmSymbols::nthreads_name(), int_signature, false); \ | |
macro(_ngroups_offset, k, vmSymbols::ngroups_name(), int_signature, false) | |
void java_lang_ThreadGroup::compute_offsets() { | |
assert(_parent_offset == 0, "offsets should be initialized only once"); | |
InstanceKlass* k = SystemDictionary::ThreadGroup_klass(); | |
THREADGROUP_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_ThreadGroup::serialize_offsets(SerializeClosure* f) { | |
THREADGROUP_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
#define THROWABLE_FIELDS_DO(macro) \ | |
macro(backtrace_offset, k, "backtrace", object_signature, false); \ | |
macro(detailMessage_offset, k, "detailMessage", string_signature, false); \ | |
macro(stackTrace_offset, k, "stackTrace", java_lang_StackTraceElement_array, false); \ | |
macro(depth_offset, k, "depth", int_signature, false); \ | |
macro(static_unassigned_stacktrace_offset, k, "UNASSIGNED_STACK", java_lang_StackTraceElement_array, true) | |
void java_lang_Throwable::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::Throwable_klass(); | |
THROWABLE_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_Throwable::serialize_offsets(SerializeClosure* f) { | |
THROWABLE_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
oop java_lang_Throwable::unassigned_stacktrace() { | |
InstanceKlass* ik = SystemDictionary::Throwable_klass(); | |
oop base = ik->static_field_base_raw(); | |
return base->obj_field(static_unassigned_stacktrace_offset); | |
} | |
oop java_lang_Throwable::backtrace(oop throwable) { | |
return throwable->obj_field_acquire(backtrace_offset); | |
} | |
void java_lang_Throwable::set_backtrace(oop throwable, oop value) { | |
throwable->release_obj_field_put(backtrace_offset, value); | |
} | |
int java_lang_Throwable::depth(oop throwable) { | |
return throwable->int_field(depth_offset); | |
} | |
void java_lang_Throwable::set_depth(oop throwable, int value) { | |
throwable->int_field_put(depth_offset, value); | |
} | |
oop java_lang_Throwable::message(oop throwable) { | |
return throwable->obj_field(detailMessage_offset); | |
} | |
// Return Symbol for detailed_message or NULL | |
Symbol* java_lang_Throwable::detail_message(oop throwable) { | |
PRESERVE_EXCEPTION_MARK; // Keep original exception | |
oop detailed_message = java_lang_Throwable::message(throwable); | |
if (detailed_message != NULL) { | |
return java_lang_String::as_symbol(detailed_message, THREAD); | |
} | |
return NULL; | |
} | |
void java_lang_Throwable::set_message(oop throwable, oop value) { | |
throwable->obj_field_put(detailMessage_offset, value); | |
} | |
void java_lang_Throwable::set_stacktrace(oop throwable, oop st_element_array) { | |
throwable->obj_field_put(stackTrace_offset, st_element_array); | |
} | |
void java_lang_Throwable::clear_stacktrace(oop throwable) { | |
set_stacktrace(throwable, NULL); | |
} | |
void java_lang_Throwable::print(oop throwable, outputStream* st) { | |
ResourceMark rm; | |
Klass* k = throwable->klass(); | |
assert(k != NULL, "just checking"); | |
st->print("%s", k->external_name()); | |
oop msg = message(throwable); | |
if (msg != NULL) { | |
st->print(": %s", java_lang_String::as_utf8_string(msg)); | |
} | |
} | |
// After this many redefines, the stack trace is unreliable. | |
const int MAX_VERSION = USHRT_MAX; | |
static inline bool version_matches(Method* method, int version) { | |
assert(version < MAX_VERSION, "version is too big"); | |
return method != NULL && (method->constants()->version() == version); | |
} | |
// This class provides a simple wrapper over the internal structure of | |
// exception backtrace to insulate users of the backtrace from needing | |
// to know what it looks like. | |
class BacktraceBuilder: public StackObj { | |
friend class BacktraceIterator; | |
private: | |
Handle _backtrace; | |
objArrayOop _head; | |
typeArrayOop _methods; | |
typeArrayOop _bcis; | |
objArrayOop _mirrors; | |
typeArrayOop _names; // needed to insulate method name against redefinition | |
int _index; | |
NoSafepointVerifier _nsv; | |
enum { | |
trace_methods_offset = java_lang_Throwable::trace_methods_offset, | |
trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, | |
trace_mirrors_offset = java_lang_Throwable::trace_mirrors_offset, | |
trace_names_offset = java_lang_Throwable::trace_names_offset, | |
trace_next_offset = java_lang_Throwable::trace_next_offset, | |
trace_size = java_lang_Throwable::trace_size, | |
trace_chunk_size = java_lang_Throwable::trace_chunk_size | |
}; | |
// get info out of chunks | |
static typeArrayOop get_methods(objArrayHandle chunk) { | |
typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset)); | |
assert(methods != NULL, "method array should be initialized in backtrace"); | |
return methods; | |
} | |
static typeArrayOop get_bcis(objArrayHandle chunk) { | |
typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); | |
assert(bcis != NULL, "bci array should be initialized in backtrace"); | |
return bcis; | |
} | |
static objArrayOop get_mirrors(objArrayHandle chunk) { | |
objArrayOop mirrors = objArrayOop(chunk->obj_at(trace_mirrors_offset)); | |
assert(mirrors != NULL, "mirror array should be initialized in backtrace"); | |
return mirrors; | |
} | |
static typeArrayOop get_names(objArrayHandle chunk) { | |
typeArrayOop names = typeArrayOop(chunk->obj_at(trace_names_offset)); | |
assert(names != NULL, "names array should be initialized in backtrace"); | |
return names; | |
} | |
public: | |
// constructor for new backtrace | |
BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL), _mirrors(NULL), _names(NULL) { | |
expand(CHECK); | |
_backtrace = Handle(THREAD, _head); | |
_index = 0; | |
} | |
BacktraceBuilder(Thread* thread, objArrayHandle backtrace) { | |
_methods = get_methods(backtrace); | |
_bcis = get_bcis(backtrace); | |
_mirrors = get_mirrors(backtrace); | |
_names = get_names(backtrace); | |
assert(_methods->length() == _bcis->length() && | |
_methods->length() == _mirrors->length() && | |
_mirrors->length() == _names->length(), | |
"method and source information arrays should match"); | |
// head is the preallocated backtrace | |
_head = backtrace(); | |
_backtrace = Handle(thread, _head); | |
_index = 0; | |
} | |
void expand(TRAPS) { | |
objArrayHandle old_head(THREAD, _head); | |
PauseNoSafepointVerifier pnsv(&_nsv); | |
objArrayOop head = oopFactory::new_objectArray(trace_size, CHECK); | |
objArrayHandle new_head(THREAD, head); | |
typeArrayOop methods = oopFactory::new_shortArray(trace_chunk_size, CHECK); | |
typeArrayHandle new_methods(THREAD, methods); | |
typeArrayOop bcis = oopFactory::new_intArray(trace_chunk_size, CHECK); | |
typeArrayHandle new_bcis(THREAD, bcis); | |
objArrayOop mirrors = oopFactory::new_objectArray(trace_chunk_size, CHECK); | |
objArrayHandle new_mirrors(THREAD, mirrors); | |
typeArrayOop names = oopFactory::new_symbolArray(trace_chunk_size, CHECK); | |
typeArrayHandle new_names(THREAD, names); | |
if (!old_head.is_null()) { | |
old_head->obj_at_put(trace_next_offset, new_head()); | |
} | |
new_head->obj_at_put(trace_methods_offset, new_methods()); | |
new_head->obj_at_put(trace_bcis_offset, new_bcis()); | |
new_head->obj_at_put(trace_mirrors_offset, new_mirrors()); | |
new_head->obj_at_put(trace_names_offset, new_names()); | |
_head = new_head(); | |
_methods = new_methods(); | |
_bcis = new_bcis(); | |
_mirrors = new_mirrors(); | |
_names = new_names(); | |
_index = 0; | |
} | |
oop backtrace() { | |
return _backtrace(); | |
} | |
inline void push(Method* method, int bci, TRAPS) { | |
// Smear the -1 bci to 0 since the array only holds unsigned | |
// shorts. The later line number lookup would just smear the -1 | |
// to a 0 even if it could be recorded. | |
if (bci == SynchronizationEntryBCI) bci = 0; | |
if (_index >= trace_chunk_size) { | |
methodHandle mhandle(THREAD, method); | |
expand(CHECK); | |
method = mhandle(); | |
} | |
_methods->ushort_at_put(_index, method->orig_method_idnum()); | |
_bcis->int_at_put(_index, Backtrace::merge_bci_and_version(bci, method->constants()->version())); | |
// Note:this doesn't leak symbols because the mirror in the backtrace keeps the | |
// klass owning the symbols alive so their refcounts aren't decremented. | |
Symbol* name = method->name(); | |
_names->symbol_at_put(_index, name); | |
// We need to save the mirrors in the backtrace to keep the class | |
// from being unloaded while we still have this stack trace. | |
assert(method->method_holder()->java_mirror() != NULL, "never push null for mirror"); | |
_mirrors->obj_at_put(_index, method->method_holder()->java_mirror()); | |
_index++; | |
} | |
}; | |
struct BacktraceElement : public StackObj { | |
int _method_id; | |
int _bci; | |
int _version; | |
Symbol* _name; | |
Handle _mirror; | |
BacktraceElement(Handle mirror, int mid, int version, int bci, Symbol* name) : | |
_mirror(mirror), _method_id(mid), _version(version), _bci(bci), _name(name) {} | |
}; | |
class BacktraceIterator : public StackObj { | |
int _index; | |
objArrayHandle _result; | |
objArrayHandle _mirrors; | |
typeArrayHandle _methods; | |
typeArrayHandle _bcis; | |
typeArrayHandle _names; | |
void init(objArrayHandle result, Thread* thread) { | |
// Get method id, bci, version and mirror from chunk | |
_result = result; | |
if (_result.not_null()) { | |
_methods = typeArrayHandle(thread, BacktraceBuilder::get_methods(_result)); | |
_bcis = typeArrayHandle(thread, BacktraceBuilder::get_bcis(_result)); | |
_mirrors = objArrayHandle(thread, BacktraceBuilder::get_mirrors(_result)); | |
_names = typeArrayHandle(thread, BacktraceBuilder::get_names(_result)); | |
_index = 0; | |
} | |
} | |
public: | |
BacktraceIterator(objArrayHandle result, Thread* thread) { | |
init(result, thread); | |
assert(_methods.is_null() || _methods->length() == java_lang_Throwable::trace_chunk_size, "lengths don't match"); | |
} | |
BacktraceElement next(Thread* thread) { | |
BacktraceElement e (Handle(thread, _mirrors->obj_at(_index)), | |
_methods->ushort_at(_index), | |
Backtrace::version_at(_bcis->int_at(_index)), | |
Backtrace::bci_at(_bcis->int_at(_index)), | |
_names->symbol_at(_index)); | |
_index++; | |
if (_index >= java_lang_Throwable::trace_chunk_size) { | |
int next_offset = java_lang_Throwable::trace_next_offset; | |
// Get next chunk | |
objArrayHandle result (thread, objArrayOop(_result->obj_at(next_offset))); | |
init(result, thread); | |
} | |
return e; | |
} | |
bool repeat() { | |
return _result.not_null() && _mirrors->obj_at(_index) != NULL; | |
} | |
}; | |
// Print stack trace element to resource allocated buffer | |
static void print_stack_element_to_stream(outputStream* st, Handle mirror, int method_id, | |
int version, int bci, Symbol* name) { | |
ResourceMark rm; | |
// Get strings and string lengths | |
InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); | |
const char* klass_name = holder->external_name(); | |
int buf_len = (int)strlen(klass_name); | |
char* method_name = name->as_C_string(); | |
buf_len += (int)strlen(method_name); | |
char* source_file_name = NULL; | |
Symbol* source = Backtrace::get_source_file_name(holder, version); | |
if (source != NULL) { | |
source_file_name = source->as_C_string(); | |
buf_len += (int)strlen(source_file_name); | |
} | |
char *module_name = NULL, *module_version = NULL; | |
ModuleEntry* module = holder->module(); | |
if (module->is_named()) { | |
module_name = module->name()->as_C_string(); | |
buf_len += (int)strlen(module_name); | |
if (module->version() != NULL) { | |
module_version = module->version()->as_C_string(); | |
buf_len += (int)strlen(module_version); | |
} | |
} | |
// Allocate temporary buffer with extra space for formatting and line number | |
char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64); | |
// Print stack trace line in buffer | |
sprintf(buf, "\tat %s.%s(", klass_name, method_name); | |
// Print module information | |
if (module_name != NULL) { | |
if (module_version != NULL) { | |
sprintf(buf + (int)strlen(buf), "%s@%s/", module_name, module_version); | |
} else { | |
sprintf(buf + (int)strlen(buf), "%s/", module_name); | |
} | |
} | |
// The method can be NULL if the requested class version is gone | |
Method* method = holder->method_with_orig_idnum(method_id, version); | |
if (!version_matches(method, version)) { | |
strcat(buf, "Redefined)"); | |
} else { | |
int line_number = Backtrace::get_line_number(method, bci); | |
if (line_number == -2) { | |
strcat(buf, "Native Method)"); | |
} else { | |
if (source_file_name != NULL && (line_number != -1)) { | |
// Sourcename and linenumber | |
sprintf(buf + (int)strlen(buf), "%s:%d)", source_file_name, line_number); | |
} else if (source_file_name != NULL) { | |
// Just sourcename | |
sprintf(buf + (int)strlen(buf), "%s)", source_file_name); | |
} else { | |
// Neither sourcename nor linenumber | |
sprintf(buf + (int)strlen(buf), "Unknown Source)"); | |
} | |
CompiledMethod* nm = method->code(); | |
if (WizardMode && nm != NULL) { | |
sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm); | |
} | |
} | |
} | |
st->print_cr("%s", buf); | |
} | |
void java_lang_Throwable::print_stack_element(outputStream *st, const methodHandle& method, int bci) { | |
Handle mirror (Thread::current(), method->method_holder()->java_mirror()); | |
int method_id = method->orig_method_idnum(); | |
int version = method->constants()->version(); | |
print_stack_element_to_stream(st, mirror, method_id, version, bci, method->name()); | |
} | |
/** | |
* Print the throwable message and its stack trace plus all causes by walking the | |
* cause chain. The output looks the same as of Throwable.printStackTrace(). | |
*/ | |
void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st) { | |
// First, print the message. | |
print(throwable(), st); | |
st->cr(); | |
// Now print the stack trace. | |
Thread* THREAD = Thread::current(); | |
while (throwable.not_null()) { | |
objArrayHandle result (THREAD, objArrayOop(backtrace(throwable()))); | |
if (result.is_null()) { | |
st->print_raw_cr("\t<<no stack trace available>>"); | |
return; | |
} | |
BacktraceIterator iter(result, THREAD); | |
while (iter.repeat()) { | |
BacktraceElement bte = iter.next(THREAD); | |
print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci, bte._name); | |
} | |
{ | |
// Call getCause() which doesn't necessarily return the _cause field. | |
EXCEPTION_MARK; | |
JavaValue cause(T_OBJECT); | |
JavaCalls::call_virtual(&cause, | |
throwable, | |
throwable->klass(), | |
vmSymbols::getCause_name(), | |
vmSymbols::void_throwable_signature(), | |
THREAD); | |
// Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. | |
if (HAS_PENDING_EXCEPTION) { | |
CLEAR_PENDING_EXCEPTION; | |
throwable = Handle(); | |
} else { | |
throwable = Handle(THREAD, (oop) cause.get_jobject()); | |
if (throwable.not_null()) { | |
st->print("Caused by: "); | |
print(throwable(), st); | |
st->cr(); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Print the throwable stack trace by calling the Java method java.lang.Throwable.printStackTrace(). | |
*/ | |
void java_lang_Throwable::java_printStackTrace(Handle throwable, TRAPS) { | |
assert(throwable->is_a(SystemDictionary::Throwable_klass()), "Throwable instance expected"); | |
JavaValue result(T_VOID); | |
JavaCalls::call_virtual(&result, | |
throwable, | |
SystemDictionary::Throwable_klass(), | |
vmSymbols::printStackTrace_name(), | |
vmSymbols::void_method_signature(), | |
THREAD); | |
} | |
void java_lang_Throwable::fill_in_stack_trace(Handle throwable, const methodHandle& method, TRAPS) { | |
if (!StackTraceInThrowable) return; | |
ResourceMark rm(THREAD); | |
// Start out by clearing the backtrace for this object, in case the VM | |
// runs out of memory while allocating the stack trace | |
set_backtrace(throwable(), NULL); | |
// Clear lazily constructed Java level stacktrace if refilling occurs | |
// This is unnecessary in 1.7+ but harmless | |
clear_stacktrace(throwable()); | |
int max_depth = MaxJavaStackTraceDepth; | |
JavaThread* thread = (JavaThread*)THREAD; | |
BacktraceBuilder bt(CHECK); | |
// If there is no Java frame just return the method that was being called | |
// with bci 0 | |
if (!thread->has_last_Java_frame()) { | |
if (max_depth >= 1 && method() != NULL) { | |
bt.push(method(), 0, CHECK); | |
log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), 1); | |
set_depth(throwable(), 1); | |
set_backtrace(throwable(), bt.backtrace()); | |
} | |
return; | |
} | |
// Instead of using vframe directly, this version of fill_in_stack_trace | |
// basically handles everything by hand. This significantly improved the | |
// speed of this method call up to 28.5% on Solaris sparc. 27.1% on Windows. | |
// See bug 6333838 for more details. | |
// The "ASSERT" here is to verify this method generates the exactly same stack | |
// trace as utilizing vframe. | |
#ifdef ASSERT | |
vframeStream st(thread); | |
methodHandle st_method(THREAD, st.method()); | |
#endif | |
int total_count = 0; | |
RegisterMap map(thread, false); | |
int decode_offset = 0; | |
CompiledMethod* nm = NULL; | |
bool skip_fillInStackTrace_check = false; | |
bool skip_throwableInit_check = false; | |
bool skip_hidden = !ShowHiddenFrames; | |
for (frame fr = thread->last_frame(); max_depth == 0 || max_depth != total_count;) { | |
Method* method = NULL; | |
int bci = 0; | |
// Compiled java method case. | |
if (decode_offset != 0) { | |
DebugInfoReadStream stream(nm, decode_offset); | |
decode_offset = stream.read_int(); | |
method = (Method*)nm->metadata_at(stream.read_int()); | |
bci = stream.read_bci(); | |
} else { | |
if (fr.is_first_frame()) break; | |
address pc = fr.pc(); | |
if (fr.is_interpreted_frame()) { | |
address bcp = fr.interpreter_frame_bcp(); | |
method = fr.interpreter_frame_method(); | |
bci = method->bci_from(bcp); | |
fr = fr.sender(&map); | |
} else { | |
CodeBlob* cb = fr.cb(); | |
// HMMM QQQ might be nice to have frame return nm as NULL if cb is non-NULL | |
// but non nmethod | |
fr = fr.sender(&map); | |
if (cb == NULL || !cb->is_compiled()) { | |
continue; | |
} | |
nm = cb->as_compiled_method(); | |
if (nm->method()->is_native()) { | |
method = nm->method(); | |
bci = 0; | |
} else { | |
PcDesc* pd = nm->pc_desc_at(pc); | |
decode_offset = pd->scope_decode_offset(); | |
// if decode_offset is not equal to 0, it will execute the | |
// "compiled java method case" at the beginning of the loop. | |
continue; | |
} | |
} | |
} | |
#ifdef ASSERT | |
assert(st_method() == method && st.bci() == bci, | |
"Wrong stack trace"); | |
st.next(); | |
// vframeStream::method isn't GC-safe so store off a copy | |
// of the Method* in case we GC. | |
if (!st.at_end()) { | |
st_method = st.method(); | |
} | |
#endif | |
// the format of the stacktrace will be: | |
// - 1 or more fillInStackTrace frames for the exception class (skipped) | |
// - 0 or more <init> methods for the exception class (skipped) | |
// - rest of the stack | |
if (!skip_fillInStackTrace_check) { | |
if (method->name() == vmSymbols::fillInStackTrace_name() && | |
throwable->is_a(method->method_holder())) { | |
continue; | |
} | |
else { | |
skip_fillInStackTrace_check = true; // gone past them all | |
} | |
} | |
if (!skip_throwableInit_check) { | |
assert(skip_fillInStackTrace_check, "logic error in backtrace filtering"); | |
// skip <init> methods of the exception class and superclasses | |
// This is simlar to classic VM. | |
if (method->name() == vmSymbols::object_initializer_name() && | |
throwable->is_a(method->method_holder())) { | |
continue; | |
} else { | |
// there are none or we've seen them all - either way stop checking | |
skip_throwableInit_check = true; | |
} | |
} | |
if (method->is_hidden()) { | |
if (skip_hidden) continue; | |
} | |
bt.push(method, bci, CHECK); | |
total_count++; | |
} | |
log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), total_count); | |
// Put completed stack trace into throwable object | |
set_backtrace(throwable(), bt.backtrace()); | |
set_depth(throwable(), total_count); | |
} | |
void java_lang_Throwable::fill_in_stack_trace(Handle throwable, const methodHandle& method) { | |
// No-op if stack trace is disabled | |
if (!StackTraceInThrowable) { | |
return; | |
} | |
// Disable stack traces for some preallocated out of memory errors | |
if (!Universe::should_fill_in_stack_trace(throwable)) { | |
return; | |
} | |
PRESERVE_EXCEPTION_MARK; | |
JavaThread* thread = JavaThread::active(); | |
fill_in_stack_trace(throwable, method, thread); | |
// ignore exceptions thrown during stack trace filling | |
CLEAR_PENDING_EXCEPTION; | |
} | |
void java_lang_Throwable::allocate_backtrace(Handle throwable, TRAPS) { | |
// Allocate stack trace - backtrace is created but not filled in | |
// No-op if stack trace is disabled | |
if (!StackTraceInThrowable) return; | |
BacktraceBuilder bt(CHECK); // creates a backtrace | |
set_backtrace(throwable(), bt.backtrace()); | |
} | |
void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle throwable) { | |
// Fill in stack trace into preallocated backtrace (no GC) | |
// No-op if stack trace is disabled | |
if (!StackTraceInThrowable) return; | |
assert(throwable->is_a(SystemDictionary::Throwable_klass()), "sanity check"); | |
JavaThread* THREAD = JavaThread::current(); | |
objArrayHandle backtrace (THREAD, (objArrayOop)java_lang_Throwable::backtrace(throwable())); | |
assert(backtrace.not_null(), "backtrace should have been preallocated"); | |
ResourceMark rm(THREAD); | |
vframeStream st(THREAD); | |
BacktraceBuilder bt(THREAD, backtrace); | |
// Unlike fill_in_stack_trace we do not skip fillInStackTrace or throwable init | |
// methods as preallocated errors aren't created by "java" code. | |
// fill in as much stack trace as possible | |
int chunk_count = 0; | |
for (;!st.at_end(); st.next()) { | |
bt.push(st.method(), st.bci(), CHECK); | |
chunk_count++; | |
// Bail-out for deep stacks | |
if (chunk_count >= trace_chunk_size) break; | |
} | |
set_depth(throwable(), chunk_count); | |
log_info(stacktrace)("%s, %d", throwable->klass()->external_name(), chunk_count); | |
// We support the Throwable immutability protocol defined for Java 7. | |
java_lang_Throwable::set_stacktrace(throwable(), java_lang_Throwable::unassigned_stacktrace()); | |
assert(java_lang_Throwable::unassigned_stacktrace() != NULL, "not initialized"); | |
} | |
void java_lang_Throwable::get_stack_trace_elements(Handle throwable, | |
objArrayHandle stack_trace_array_h, TRAPS) { | |
if (throwable.is_null() || stack_trace_array_h.is_null()) { | |
THROW(vmSymbols::java_lang_NullPointerException()); | |
} | |
assert(stack_trace_array_h->is_objArray(), "Stack trace array should be an array of StackTraceElenent"); | |
if (stack_trace_array_h->length() != depth(throwable())) { | |
THROW(vmSymbols::java_lang_IndexOutOfBoundsException()); | |
} | |
objArrayHandle result(THREAD, objArrayOop(backtrace(throwable()))); | |
BacktraceIterator iter(result, THREAD); | |
int index = 0; | |
while (iter.repeat()) { | |
BacktraceElement bte = iter.next(THREAD); | |
Handle stack_trace_element(THREAD, stack_trace_array_h->obj_at(index++)); | |
if (stack_trace_element.is_null()) { | |
THROW(vmSymbols::java_lang_NullPointerException()); | |
} | |
InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(bte._mirror())); | |
methodHandle method (THREAD, holder->method_with_orig_idnum(bte._method_id, bte._version)); | |
java_lang_StackTraceElement::fill_in(stack_trace_element, holder, | |
method, | |
bte._version, | |
bte._bci, | |
bte._name, CHECK); | |
} | |
} | |
oop java_lang_StackTraceElement::create(const methodHandle& method, int bci, TRAPS) { | |
// Allocate java.lang.StackTraceElement instance | |
InstanceKlass* k = SystemDictionary::StackTraceElement_klass(); | |
assert(k != NULL, "must be loaded in 1.4+"); | |
if (k->should_be_initialized()) { | |
k->initialize(CHECK_0); | |
} | |
Handle element = k->allocate_instance_handle(CHECK_0); | |
int version = method->constants()->version(); | |
fill_in(element, method->method_holder(), method, version, bci, method->name(), CHECK_0); | |
return element(); | |
} | |
void java_lang_StackTraceElement::fill_in(Handle element, | |
InstanceKlass* holder, const methodHandle& method, | |
int version, int bci, Symbol* name, TRAPS) { | |
assert(element->is_a(SystemDictionary::StackTraceElement_klass()), "sanity check"); | |
ResourceMark rm(THREAD); | |
HandleMark hm(THREAD); | |
// Fill in class name | |
Handle java_class(THREAD, holder->java_mirror()); | |
oop classname = java_lang_Class::name(java_class, CHECK); | |
java_lang_StackTraceElement::set_declaringClass(element(), classname); | |
java_lang_StackTraceElement::set_declaringClassObject(element(), java_class()); | |
oop loader = holder->class_loader(); | |
if (loader != NULL) { | |
oop loader_name = java_lang_ClassLoader::name(loader); | |
if (loader_name != NULL) | |
java_lang_StackTraceElement::set_classLoaderName(element(), loader_name); | |
} | |
// Fill in method name | |
oop methodname = StringTable::intern(name, CHECK); | |
java_lang_StackTraceElement::set_methodName(element(), methodname); | |
// Fill in module name and version | |
ModuleEntry* module = holder->module(); | |
if (module->is_named()) { | |
oop module_name = StringTable::intern(module->name(), CHECK); | |
java_lang_StackTraceElement::set_moduleName(element(), module_name); | |
oop module_version; | |
if (module->version() != NULL) { | |
module_version = StringTable::intern(module->version(), CHECK); | |
} else { | |
module_version = NULL; | |
} | |
java_lang_StackTraceElement::set_moduleVersion(element(), module_version); | |
} | |
if (method() == NULL || !version_matches(method(), version)) { | |
// The method was redefined, accurate line number information isn't available | |
java_lang_StackTraceElement::set_fileName(element(), NULL); | |
java_lang_StackTraceElement::set_lineNumber(element(), -1); | |
} else { | |
// Fill in source file name and line number. | |
Symbol* source = Backtrace::get_source_file_name(holder, version); | |
oop source_file = java_lang_Class::source_file(java_class()); | |
if (source != NULL) { | |
// Class was not redefined. We can trust its cache if set, | |
// else we have to initialize it. | |
if (source_file == NULL) { | |
source_file = StringTable::intern(source, CHECK); | |
java_lang_Class::set_source_file(java_class(), source_file); | |
} | |
} else { | |
// Class was redefined. Dump the cache if it was set. | |
if (source_file != NULL) { | |
source_file = NULL; | |
java_lang_Class::set_source_file(java_class(), source_file); | |
} | |
} | |
java_lang_StackTraceElement::set_fileName(element(), source_file); | |
int line_number = Backtrace::get_line_number(method, bci); | |
java_lang_StackTraceElement::set_lineNumber(element(), line_number); | |
} | |
} | |
Method* java_lang_StackFrameInfo::get_method(Handle stackFrame, InstanceKlass* holder, TRAPS) { | |
Handle mname(THREAD, stackFrame->obj_field(_memberName_offset)); | |
Method* method = (Method*)java_lang_invoke_MemberName::vmtarget(mname()); | |
// we should expand MemberName::name when Throwable uses StackTrace | |
// MethodHandles::expand_MemberName(mname, MethodHandles::_suppress_defc|MethodHandles::_suppress_type, CHECK_NULL); | |
return method; | |
} | |
void java_lang_StackFrameInfo::set_method_and_bci(Handle stackFrame, const methodHandle& method, int bci, TRAPS) { | |
// set Method* or mid/cpref | |
Handle mname(Thread::current(), stackFrame->obj_field(_memberName_offset)); | |
InstanceKlass* ik = method->method_holder(); | |
CallInfo info(method(), ik, CHECK); | |
MethodHandles::init_method_MemberName(mname, info); | |
// set bci | |
java_lang_StackFrameInfo::set_bci(stackFrame(), bci); | |
// method may be redefined; store the version | |
int version = method->constants()->version(); | |
assert((jushort)version == version, "version should be short"); | |
java_lang_StackFrameInfo::set_version(stackFrame(), (short)version); | |
} | |
void java_lang_StackFrameInfo::to_stack_trace_element(Handle stackFrame, Handle stack_trace_element, TRAPS) { | |
ResourceMark rm(THREAD); | |
Handle mname(THREAD, stackFrame->obj_field(java_lang_StackFrameInfo::_memberName_offset)); | |
Klass* clazz = java_lang_Class::as_Klass(java_lang_invoke_MemberName::clazz(mname())); | |
InstanceKlass* holder = InstanceKlass::cast(clazz); | |
Method* method = java_lang_StackFrameInfo::get_method(stackFrame, holder, CHECK); | |
short version = stackFrame->short_field(_version_offset); | |
int bci = stackFrame->int_field(_bci_offset); | |
Symbol* name = method->name(); | |
java_lang_StackTraceElement::fill_in(stack_trace_element, holder, method, version, bci, name, CHECK); | |
} | |
#define STACKFRAMEINFO_FIELDS_DO(macro) \ | |
macro(_memberName_offset, k, "memberName", object_signature, false); \ | |
macro(_bci_offset, k, "bci", int_signature, false) | |
void java_lang_StackFrameInfo::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::StackFrameInfo_klass(); | |
STACKFRAMEINFO_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
STACKFRAMEINFO_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_StackFrameInfo::serialize_offsets(SerializeClosure* f) { | |
STACKFRAMEINFO_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
STACKFRAMEINFO_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
#define LIVESTACKFRAMEINFO_FIELDS_DO(macro) \ | |
macro(_monitors_offset, k, "monitors", object_array_signature, false); \ | |
macro(_locals_offset, k, "locals", object_array_signature, false); \ | |
macro(_operands_offset, k, "operands", object_array_signature, false); \ | |
macro(_mode_offset, k, "mode", int_signature, false) | |
void java_lang_LiveStackFrameInfo::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::LiveStackFrameInfo_klass(); | |
LIVESTACKFRAMEINFO_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_LiveStackFrameInfo::serialize_offsets(SerializeClosure* f) { | |
LIVESTACKFRAMEINFO_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
#define ACCESSIBLEOBJECT_FIELDS_DO(macro) \ | |
macro(override_offset, k, "override", bool_signature, false) | |
void java_lang_reflect_AccessibleObject::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_AccessibleObject_klass(); | |
ACCESSIBLEOBJECT_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_reflect_AccessibleObject::serialize_offsets(SerializeClosure* f) { | |
ACCESSIBLEOBJECT_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
jboolean java_lang_reflect_AccessibleObject::override(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return (jboolean) reflect->bool_field(override_offset); | |
} | |
void java_lang_reflect_AccessibleObject::set_override(oop reflect, jboolean value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->bool_field_put(override_offset, (int) value); | |
} | |
#define METHOD_FIELDS_DO(macro) \ | |
macro(clazz_offset, k, vmSymbols::clazz_name(), class_signature, false); \ | |
macro(name_offset, k, vmSymbols::name_name(), string_signature, false); \ | |
macro(returnType_offset, k, vmSymbols::returnType_name(), class_signature, false); \ | |
macro(parameterTypes_offset, k, vmSymbols::parameterTypes_name(), class_array_signature, false); \ | |
macro(exceptionTypes_offset, k, vmSymbols::exceptionTypes_name(), class_array_signature, false); \ | |
macro(slot_offset, k, vmSymbols::slot_name(), int_signature, false); \ | |
macro(modifiers_offset, k, vmSymbols::modifiers_name(), int_signature, false); \ | |
macro##_OPTIONAL(signature_offset, k, vmSymbols::signature_name(), string_signature); \ | |
macro##_OPTIONAL(annotations_offset, k, vmSymbols::annotations_name(), byte_array_signature); \ | |
macro##_OPTIONAL(parameter_annotations_offset, k, vmSymbols::parameter_annotations_name(), byte_array_signature); \ | |
macro##_OPTIONAL(annotation_default_offset, k, vmSymbols::annotation_default_name(), byte_array_signature); \ | |
macro##_OPTIONAL(type_annotations_offset, k, vmSymbols::type_annotations_name(), byte_array_signature) | |
void java_lang_reflect_Method::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_Method_klass(); | |
// The generic signature and annotations fields are only present in 1.5 | |
signature_offset = -1; | |
annotations_offset = -1; | |
parameter_annotations_offset = -1; | |
annotation_default_offset = -1; | |
type_annotations_offset = -1; | |
METHOD_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_reflect_Method::serialize_offsets(SerializeClosure* f) { | |
METHOD_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
Handle java_lang_reflect_Method::create(TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
Klass* klass = SystemDictionary::reflect_Method_klass(); | |
// This class is eagerly initialized during VM initialization, since we keep a refence | |
// to one of the methods | |
assert(InstanceKlass::cast(klass)->is_initialized(), "must be initialized"); | |
return InstanceKlass::cast(klass)->allocate_instance_handle(THREAD); | |
} | |
oop java_lang_reflect_Method::clazz(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->obj_field(clazz_offset); | |
} | |
void java_lang_reflect_Method::set_clazz(oop reflect, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->obj_field_put(clazz_offset, value); | |
} | |
int java_lang_reflect_Method::slot(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->int_field(slot_offset); | |
} | |
void java_lang_reflect_Method::set_slot(oop reflect, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->int_field_put(slot_offset, value); | |
} | |
oop java_lang_reflect_Method::name(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return method->obj_field(name_offset); | |
} | |
void java_lang_reflect_Method::set_name(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
method->obj_field_put(name_offset, value); | |
} | |
oop java_lang_reflect_Method::return_type(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return method->obj_field(returnType_offset); | |
} | |
void java_lang_reflect_Method::set_return_type(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
method->obj_field_put(returnType_offset, value); | |
} | |
oop java_lang_reflect_Method::parameter_types(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return method->obj_field(parameterTypes_offset); | |
} | |
void java_lang_reflect_Method::set_parameter_types(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
method->obj_field_put(parameterTypes_offset, value); | |
} | |
oop java_lang_reflect_Method::exception_types(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return method->obj_field(exceptionTypes_offset); | |
} | |
void java_lang_reflect_Method::set_exception_types(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
method->obj_field_put(exceptionTypes_offset, value); | |
} | |
int java_lang_reflect_Method::modifiers(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return method->int_field(modifiers_offset); | |
} | |
void java_lang_reflect_Method::set_modifiers(oop method, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
method->int_field_put(modifiers_offset, value); | |
} | |
bool java_lang_reflect_Method::has_signature_field() { | |
return (signature_offset >= 0); | |
} | |
oop java_lang_reflect_Method::signature(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
return method->obj_field(signature_offset); | |
} | |
void java_lang_reflect_Method::set_signature(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
method->obj_field_put(signature_offset, value); | |
} | |
bool java_lang_reflect_Method::has_annotations_field() { | |
return (annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Method::annotations(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
return method->obj_field(annotations_offset); | |
} | |
void java_lang_reflect_Method::set_annotations(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
method->obj_field_put(annotations_offset, value); | |
} | |
bool java_lang_reflect_Method::has_parameter_annotations_field() { | |
return (parameter_annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Method::parameter_annotations(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_parameter_annotations_field(), "parameter annotations field must be present"); | |
return method->obj_field(parameter_annotations_offset); | |
} | |
void java_lang_reflect_Method::set_parameter_annotations(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_parameter_annotations_field(), "parameter annotations field must be present"); | |
method->obj_field_put(parameter_annotations_offset, value); | |
} | |
bool java_lang_reflect_Method::has_annotation_default_field() { | |
return (annotation_default_offset >= 0); | |
} | |
oop java_lang_reflect_Method::annotation_default(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotation_default_field(), "annotation default field must be present"); | |
return method->obj_field(annotation_default_offset); | |
} | |
void java_lang_reflect_Method::set_annotation_default(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotation_default_field(), "annotation default field must be present"); | |
method->obj_field_put(annotation_default_offset, value); | |
} | |
bool java_lang_reflect_Method::has_type_annotations_field() { | |
return (type_annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Method::type_annotations(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
return method->obj_field(type_annotations_offset); | |
} | |
void java_lang_reflect_Method::set_type_annotations(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
method->obj_field_put(type_annotations_offset, value); | |
} | |
#define CONSTRUCTOR_FIELDS_DO(macro) \ | |
macro(clazz_offset, k, vmSymbols::clazz_name(), class_signature, false); \ | |
macro(parameterTypes_offset, k, vmSymbols::parameterTypes_name(), class_array_signature, false); \ | |
macro(exceptionTypes_offset, k, vmSymbols::exceptionTypes_name(), class_array_signature, false); \ | |
macro(slot_offset, k, vmSymbols::slot_name(), int_signature, false); \ | |
macro(modifiers_offset, k, vmSymbols::modifiers_name(), int_signature, false); \ | |
macro##_OPTIONAL(signature_offset, k, vmSymbols::signature_name(), string_signature); \ | |
macro##_OPTIONAL(annotations_offset, k, vmSymbols::annotations_name(), byte_array_signature); \ | |
macro##_OPTIONAL(parameter_annotations_offset, k, vmSymbols::parameter_annotations_name(), byte_array_signature); \ | |
macro##_OPTIONAL(type_annotations_offset, k, vmSymbols::type_annotations_name(), byte_array_signature) | |
void java_lang_reflect_Constructor::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_Constructor_klass(); | |
// The generic signature and annotations fields are only present in 1.5 | |
signature_offset = -1; | |
annotations_offset = -1; | |
parameter_annotations_offset = -1; | |
type_annotations_offset = -1; | |
CONSTRUCTOR_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_reflect_Constructor::serialize_offsets(SerializeClosure* f) { | |
CONSTRUCTOR_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
Handle java_lang_reflect_Constructor::create(TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
Symbol* name = vmSymbols::java_lang_reflect_Constructor(); | |
Klass* k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH); | |
InstanceKlass* ik = InstanceKlass::cast(k); | |
// Ensure it is initialized | |
ik->initialize(CHECK_NH); | |
return ik->allocate_instance_handle(THREAD); | |
} | |
oop java_lang_reflect_Constructor::clazz(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->obj_field(clazz_offset); | |
} | |
void java_lang_reflect_Constructor::set_clazz(oop reflect, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->obj_field_put(clazz_offset, value); | |
} | |
oop java_lang_reflect_Constructor::parameter_types(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return constructor->obj_field(parameterTypes_offset); | |
} | |
void java_lang_reflect_Constructor::set_parameter_types(oop constructor, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
constructor->obj_field_put(parameterTypes_offset, value); | |
} | |
oop java_lang_reflect_Constructor::exception_types(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return constructor->obj_field(exceptionTypes_offset); | |
} | |
void java_lang_reflect_Constructor::set_exception_types(oop constructor, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
constructor->obj_field_put(exceptionTypes_offset, value); | |
} | |
int java_lang_reflect_Constructor::slot(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->int_field(slot_offset); | |
} | |
void java_lang_reflect_Constructor::set_slot(oop reflect, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->int_field_put(slot_offset, value); | |
} | |
int java_lang_reflect_Constructor::modifiers(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return constructor->int_field(modifiers_offset); | |
} | |
void java_lang_reflect_Constructor::set_modifiers(oop constructor, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
constructor->int_field_put(modifiers_offset, value); | |
} | |
bool java_lang_reflect_Constructor::has_signature_field() { | |
return (signature_offset >= 0); | |
} | |
oop java_lang_reflect_Constructor::signature(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
return constructor->obj_field(signature_offset); | |
} | |
void java_lang_reflect_Constructor::set_signature(oop constructor, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
constructor->obj_field_put(signature_offset, value); | |
} | |
bool java_lang_reflect_Constructor::has_annotations_field() { | |
return (annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Constructor::annotations(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
return constructor->obj_field(annotations_offset); | |
} | |
void java_lang_reflect_Constructor::set_annotations(oop constructor, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
constructor->obj_field_put(annotations_offset, value); | |
} | |
bool java_lang_reflect_Constructor::has_parameter_annotations_field() { | |
return (parameter_annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Constructor::parameter_annotations(oop method) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_parameter_annotations_field(), "parameter annotations field must be present"); | |
return method->obj_field(parameter_annotations_offset); | |
} | |
void java_lang_reflect_Constructor::set_parameter_annotations(oop method, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_parameter_annotations_field(), "parameter annotations field must be present"); | |
method->obj_field_put(parameter_annotations_offset, value); | |
} | |
bool java_lang_reflect_Constructor::has_type_annotations_field() { | |
return (type_annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Constructor::type_annotations(oop constructor) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
return constructor->obj_field(type_annotations_offset); | |
} | |
void java_lang_reflect_Constructor::set_type_annotations(oop constructor, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
constructor->obj_field_put(type_annotations_offset, value); | |
} | |
#define FIELD_FIELDS_DO(macro) \ | |
macro(clazz_offset, k, vmSymbols::clazz_name(), class_signature, false); \ | |
macro(name_offset, k, vmSymbols::name_name(), string_signature, false); \ | |
macro(type_offset, k, vmSymbols::type_name(), class_signature, false); \ | |
macro(slot_offset, k, vmSymbols::slot_name(), int_signature, false); \ | |
macro(modifiers_offset, k, vmSymbols::modifiers_name(), int_signature, false); \ | |
macro##_OPTIONAL(signature_offset, k, vmSymbols::signature_name(), string_signature); \ | |
macro##_OPTIONAL(annotations_offset, k, vmSymbols::annotations_name(), byte_array_signature); \ | |
macro##_OPTIONAL(type_annotations_offset, k, vmSymbols::type_annotations_name(), byte_array_signature) | |
void java_lang_reflect_Field::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_Field_klass(); | |
// The generic signature and annotations fields are only present in 1.5 | |
signature_offset = -1; | |
annotations_offset = -1; | |
type_annotations_offset = -1; | |
FIELD_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_reflect_Field::serialize_offsets(SerializeClosure* f) { | |
FIELD_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
Handle java_lang_reflect_Field::create(TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
Symbol* name = vmSymbols::java_lang_reflect_Field(); | |
Klass* k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH); | |
InstanceKlass* ik = InstanceKlass::cast(k); | |
// Ensure it is initialized | |
ik->initialize(CHECK_NH); | |
return ik->allocate_instance_handle(THREAD); | |
} | |
oop java_lang_reflect_Field::clazz(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->obj_field(clazz_offset); | |
} | |
void java_lang_reflect_Field::set_clazz(oop reflect, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->obj_field_put(clazz_offset, value); | |
} | |
oop java_lang_reflect_Field::name(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return field->obj_field(name_offset); | |
} | |
void java_lang_reflect_Field::set_name(oop field, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
field->obj_field_put(name_offset, value); | |
} | |
oop java_lang_reflect_Field::type(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return field->obj_field(type_offset); | |
} | |
void java_lang_reflect_Field::set_type(oop field, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
field->obj_field_put(type_offset, value); | |
} | |
int java_lang_reflect_Field::slot(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return reflect->int_field(slot_offset); | |
} | |
void java_lang_reflect_Field::set_slot(oop reflect, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
reflect->int_field_put(slot_offset, value); | |
} | |
int java_lang_reflect_Field::modifiers(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return field->int_field(modifiers_offset); | |
} | |
void java_lang_reflect_Field::set_modifiers(oop field, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
field->int_field_put(modifiers_offset, value); | |
} | |
bool java_lang_reflect_Field::has_signature_field() { | |
return (signature_offset >= 0); | |
} | |
oop java_lang_reflect_Field::signature(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
return field->obj_field(signature_offset); | |
} | |
void java_lang_reflect_Field::set_signature(oop field, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_signature_field(), "signature field must be present"); | |
field->obj_field_put(signature_offset, value); | |
} | |
bool java_lang_reflect_Field::has_annotations_field() { | |
return (annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Field::annotations(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
return field->obj_field(annotations_offset); | |
} | |
void java_lang_reflect_Field::set_annotations(oop field, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_annotations_field(), "annotations field must be present"); | |
field->obj_field_put(annotations_offset, value); | |
} | |
bool java_lang_reflect_Field::has_type_annotations_field() { | |
return (type_annotations_offset >= 0); | |
} | |
oop java_lang_reflect_Field::type_annotations(oop field) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
return field->obj_field(type_annotations_offset); | |
} | |
void java_lang_reflect_Field::set_type_annotations(oop field, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
assert(has_type_annotations_field(), "type_annotations field must be present"); | |
field->obj_field_put(type_annotations_offset, value); | |
} | |
#define CONSTANTPOOL_FIELDS_DO(macro) \ | |
macro(_oop_offset, k, "constantPoolOop", object_signature, false) | |
void reflect_ConstantPool::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_ConstantPool_klass(); | |
// The field is called ConstantPool* in the sun.reflect.ConstantPool class. | |
CONSTANTPOOL_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void reflect_ConstantPool::serialize_offsets(SerializeClosure* f) { | |
CONSTANTPOOL_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
#define PARAMETER_FIELDS_DO(macro) \ | |
macro(name_offset, k, vmSymbols::name_name(), string_signature, false); \ | |
macro(modifiers_offset, k, vmSymbols::modifiers_name(), int_signature, false); \ | |
macro(index_offset, k, vmSymbols::index_name(), int_signature, false); \ | |
macro(executable_offset, k, vmSymbols::executable_name(), executable_signature, false) | |
void java_lang_reflect_Parameter::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::reflect_Parameter_klass(); | |
PARAMETER_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_reflect_Parameter::serialize_offsets(SerializeClosure* f) { | |
PARAMETER_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
Handle java_lang_reflect_Parameter::create(TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
Symbol* name = vmSymbols::java_lang_reflect_Parameter(); | |
Klass* k = SystemDictionary::resolve_or_fail(name, true, CHECK_NH); | |
InstanceKlass* ik = InstanceKlass::cast(k); | |
// Ensure it is initialized | |
ik->initialize(CHECK_NH); | |
return ik->allocate_instance_handle(THREAD); | |
} | |
oop java_lang_reflect_Parameter::name(oop param) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return param->obj_field(name_offset); | |
} | |
void java_lang_reflect_Parameter::set_name(oop param, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
param->obj_field_put(name_offset, value); | |
} | |
int java_lang_reflect_Parameter::modifiers(oop param) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return param->int_field(modifiers_offset); | |
} | |
void java_lang_reflect_Parameter::set_modifiers(oop param, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
param->int_field_put(modifiers_offset, value); | |
} | |
int java_lang_reflect_Parameter::index(oop param) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return param->int_field(index_offset); | |
} | |
void java_lang_reflect_Parameter::set_index(oop param, int value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
param->int_field_put(index_offset, value); | |
} | |
oop java_lang_reflect_Parameter::executable(oop param) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return param->obj_field(executable_offset); | |
} | |
void java_lang_reflect_Parameter::set_executable(oop param, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
param->obj_field_put(executable_offset, value); | |
} | |
int java_lang_Module::loader_offset; | |
int java_lang_Module::name_offset; | |
int java_lang_Module::_module_entry_offset = -1; | |
Handle java_lang_Module::create(Handle loader, Handle module_name, TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return JavaCalls::construct_new_instance(SystemDictionary::Module_klass(), | |
vmSymbols::java_lang_module_init_signature(), | |
loader, module_name, CHECK_NH); | |
} | |
#define MODULE_FIELDS_DO(macro) \ | |
macro(loader_offset, k, vmSymbols::loader_name(), classloader_signature, false); \ | |
macro(name_offset, k, vmSymbols::name_name(), string_signature, false) | |
void java_lang_Module::compute_offsets() { | |
InstanceKlass* k = SystemDictionary::Module_klass(); | |
MODULE_FIELDS_DO(FIELD_COMPUTE_OFFSET); | |
MODULE_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); | |
} | |
#if INCLUDE_CDS | |
void java_lang_Module::serialize_offsets(SerializeClosure* f) { | |
MODULE_FIELDS_DO(FIELD_SERIALIZE_OFFSET); | |
MODULE_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET); | |
} | |
#endif | |
oop java_lang_Module::loader(oop module) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return module->obj_field(loader_offset); | |
} | |
void java_lang_Module::set_loader(oop module, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
module->obj_field_put(loader_offset, value); | |
} | |
oop java_lang_Module::name(oop module) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
return module->obj_field(name_offset); | |
} | |
void java_lang_Module::set_name(oop module, oop value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
module->obj_field_put(name_offset, value); | |
} | |
ModuleEntry* java_lang_Module::module_entry(oop module) { | |
assert(_module_entry_offset != -1, "Uninitialized module_entry_offset"); | |
assert(module != NULL, "module can't be null"); | |
assert(oopDesc::is_oop(module), "module must be oop"); | |
ModuleEntry* module_entry = (ModuleEntry*)module->address_field(_module_entry_offset); | |
if (module_entry == NULL) { | |
// If the inject field containing the ModuleEntry* is null then return the | |
// class loader's unnamed module. | |
oop loader = java_lang_Module::loader(module); | |
Handle h_loader = Handle(Thread::current(), loader); | |
ClassLoaderData* loader_cld = SystemDictionary::register_loader(h_loader); | |
return loader_cld->unnamed_module(); | |
} | |
return module_entry; | |
} | |
void java_lang_Module::set_module_entry(oop module, ModuleEntry* module_entry) { | |
assert(_module_entry_offset != -1, "Uninitialized module_entry_offset"); | |
assert(module != NULL, "module can't be null"); | |
assert(oopDesc::is_oop(module), "module must be oop"); | |
module->address_field_put(_module_entry_offset, (address)module_entry); | |
} | |
Handle reflect_ConstantPool::create(TRAPS) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
InstanceKlass* k = SystemDictionary::reflect_ConstantPool_klass(); | |
// Ensure it is initialized | |
k->initialize(CHECK_NH); | |
return k->allocate_instance_handle(THREAD); | |
} | |
void reflect_ConstantPool::set_cp(oop reflect, ConstantPool* value) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
oop mirror = value->pool_holder()->java_mirror(); | |
// Save the mirror to get back the constant pool. | |
reflect->obj_field_put(_oop_offset, mirror); | |
} | |
ConstantPool* reflect_ConstantPool::get_cp(oop reflect) { | |
assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); | |
oop mirror = reflect->obj_field(_oop_offset); | |
Klass* k = java_lang_Class::as_Klass(mirror); | |
assert(k->is_instance_klass(), "Must be"); | |
// Get the constant pool back from the klass. Since class redefinition | |
// merges the new constant pool into the old, this is essentially the | |
// same constant pool as the original. If constant pool merging is | |
// no longer done in the future, this will have to change to save | |
// the original. | |
return InstanceKlass::cast(k)->constants(); | |
} | |
#define UNSAFESTATICFIELDACCESSORIMPL_FIELDS_DO(macro) \ | |
macro(_base_offset, k, "base", object_signature, false) | |