Skip to content
Permalink
Browse files
8261480: MetaspaceShared::preload_and_dump should check exceptions
Reviewed-by: dholmes, ccheung
  • Loading branch information
iklam committed Mar 18, 2021
1 parent 81ba578 commit 2b93ae00194d034ca906fd7731f436fec430a09d
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 259 deletions.
@@ -92,6 +92,61 @@ ClassListParser::~ClassListParser() {
_instance = NULL;
}

int ClassListParser::parse(TRAPS) {
int class_count = 0;

while (parse_one_line()) {
if (lambda_form_line()) {
// The current line is "@lambda-form-invoker ...". It has been recorded in LambdaFormInvokers,
// and will be processed later.
continue;
}

TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name);
if (_indy_items->length() > 0) {
// The current line is "@lambda-proxy class_name". Load the proxy class.
resolve_indy(THREAD, class_name_symbol);
class_count++;
continue;
}

Klass* klass = load_current_class(class_name_symbol, THREAD);
if (HAS_PENDING_EXCEPTION) {
if (PENDING_EXCEPTION->is_a(vmClasses::OutOfMemoryError_klass())) {
// If we have run out of memory, don't try to load the rest of the classes in
// the classlist. Throw an exception, which will terminate the dumping process.
return 0; // THROW
}

// We might have an invalid class name or an bad class. Warn about it
// and keep going to the next line.
CLEAR_PENDING_EXCEPTION;
log_warning(cds)("Preload Warning: Cannot find %s", _class_name);
continue;
}

assert(klass != NULL, "sanity");
if (log_is_enabled(Trace, cds)) {
ResourceMark rm(THREAD);
log_trace(cds)("Shared spaces preloaded: %s", klass->external_name());
}

if (klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);

// Link the class to cause the bytecodes to be rewritten and the
// cpcache to be created. The linking is done as soon as classes
// are loaded in order that the related data structures (klass and
// cpCache) are located together.
MetaspaceShared::try_link_class(THREAD, ik);
}

class_count++;
}

return class_count;
}

bool ClassListParser::parse_one_line() {
for (;;) {
if (fgets(_line, sizeof(_line), _file) == NULL) {
@@ -398,30 +453,27 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
if (strncmp(_class_name, "java/", 5) == 0) {
log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s",
_class_name, _source);
return NULL;
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}

InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL);
if (k->local_interfaces()->length() != _interfaces->length()) {
print_specified_interfaces();
print_actual_interfaces(k);
error("The number of interfaces (%d) specified in class list does not match the class file (%d)",
_interfaces->length(), k->local_interfaces()->length());
}

if (k != NULL) {
if (k->local_interfaces()->length() != _interfaces->length()) {
print_specified_interfaces();
print_actual_interfaces(k);
error("The number of interfaces (%d) specified in class list does not match the class file (%d)",
_interfaces->length(), k->local_interfaces()->length());
}

bool added = SystemDictionaryShared::add_unregistered_class(k, CHECK_NULL);
if (!added) {
// We allow only a single unregistered class for each unique name.
error("Duplicated class %s", _class_name);
}

// This tells JVM_FindLoadedClass to not find this class.
k->set_shared_classpath_index(UNREGISTERED_INDEX);
k->clear_shared_class_loader_type();
bool added = SystemDictionaryShared::add_unregistered_class(k, CHECK_NULL);
if (!added) {
// We allow only a single unregistered class for each unique name.
error("Duplicated class %s", _class_name);
}

// This tells JVM_FindLoadedClass to not find this class.
k->set_shared_classpath_index(UNREGISTERED_INDEX);
k->clear_shared_class_loader_type();

return k;
}

@@ -442,10 +494,8 @@ void ClassListParser::populate_cds_indy_info(const constantPoolHandle &pool, int
} else if (tag == JVM_CONSTANT_MethodHandle) {
cii->add_ref_kind(pool->method_handle_ref_kind_at(arg));
int callee_index = pool->method_handle_klass_index_at(arg);
Klass* callee = pool->klass_at(callee_index, THREAD);
if (callee != NULL) {
cii->add_item(callee->name()->as_C_string());
}
Klass* callee = pool->klass_at(callee_index, CHECK);
cii->add_item(callee->name()->as_C_string());
cii->add_item(pool->method_handle_name_ref_at(arg)->as_C_string());
cii->add_item(pool->method_handle_signature_ref_at(arg)->as_C_string());
} else {
@@ -458,7 +508,7 @@ void ClassListParser::populate_cds_indy_info(const constantPoolHandle &pool, int
bool ClassListParser::is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS) {
ResourceMark rm(THREAD);
CDSIndyInfo cii;
populate_cds_indy_info(pool, cp_index, &cii, THREAD);
populate_cds_indy_info(pool, cp_index, &cii, CHECK_0);
GrowableArray<const char*>* items = cii.items();
int indy_info_offset = 1;
if (_indy_items->length() - indy_info_offset != items->length()) {
@@ -471,10 +521,13 @@ bool ClassListParser::is_matching_cp_entry(constantPoolHandle &pool, int cp_inde
}
return true;
}
void ClassListParser::resolve_indy(Symbol* class_name_symbol, TRAPS) {

void ClassListParser::resolve_indy(Thread* current, Symbol* class_name_symbol) {
ExceptionMark em(current);
Thread* THREAD = current; // For exception macros.
ClassListParser::resolve_indy_impl(class_name_symbol, THREAD);
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm(THREAD);
ResourceMark rm(current);
char* ex_msg = (char*)"";
oop message = java_lang_Throwable::message(PENDING_EXCEPTION);
if (message != NULL) {
@@ -491,14 +544,14 @@ void ClassListParser::resolve_indy(Symbol* class_name_symbol, TRAPS) {
void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) {
Handle class_loader(THREAD, SystemDictionary::java_system_loader());
Handle protection_domain;
Klass* klass = SystemDictionary::resolve_or_fail(class_name_symbol, class_loader, protection_domain, true, CHECK); // FIXME should really be just a lookup
if (klass != NULL && klass->is_instance_klass()) {
Klass* klass = SystemDictionary::resolve_or_fail(class_name_symbol, class_loader, protection_domain, true, CHECK);
if (klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
if (SystemDictionaryShared::has_class_failed_verification(ik)) {
// don't attempt to resolve indy on classes that has previously failed verification
MetaspaceShared::try_link_class(THREAD, ik);
if (!ik->is_linked()) {
// Verification of ik has failed
return;
}
MetaspaceShared::try_link_class(ik, CHECK);

ConstantPool* cp = ik->constants();
ConstantPoolCache* cpcache = cp->cache();
@@ -538,15 +591,8 @@ void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) {
}
}

Klass* ClassListParser::load_current_class(TRAPS) {
TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name);

if (_indy_items->length() > 0) {
resolve_indy(class_name_symbol, CHECK_NULL);
return NULL;
}

Klass* klass = NULL;
Klass* ClassListParser::load_current_class(Symbol* class_name_symbol, TRAPS) {
Klass* klass;
if (!is_loading_from_source()) {
// Load classes for the boot/platform/app loaders only.
if (is_super_specified()) {
@@ -556,34 +602,29 @@ Klass* ClassListParser::load_current_class(TRAPS) {
error("If source location is not specified, interface(s) must not be specified");
}

bool non_array = !Signature::is_array(class_name_symbol);

JavaValue result(T_OBJECT);
if (non_array) {
// At this point, we are executing in the context of the boot loader. We
// cannot call Class.forName because that is context dependent and
// would load only classes for the boot loader.
//
// Instead, let's call java_system_loader().loadClass() directly, which will
// delegate to the correct loader (boot, platform or app) depending on
// the class name.

Handle s = java_lang_String::create_from_symbol(class_name_symbol, CHECK_NULL);
// ClassLoader.loadClass() wants external class name format, i.e., convert '/' chars to '.'
Handle ext_class_name = java_lang_String::externalize_classname(s, CHECK_NULL);
Handle loader = Handle(THREAD, SystemDictionary::java_system_loader());

JavaCalls::call_virtual(&result,
loader, //SystemDictionary::java_system_loader(),
vmClasses::ClassLoader_klass(),
vmSymbols::loadClass_name(),
vmSymbols::string_class_signature(),
ext_class_name,
CHECK_NULL);
} else {
if (Signature::is_array(class_name_symbol)) {
// array classes are not supported in class list.
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}

JavaValue result(T_OBJECT);
// Call java_system_loader().loadClass() directly, which will
// delegate to the correct loader (boot, platform or app) depending on
// the package name.

Handle s = java_lang_String::create_from_symbol(class_name_symbol, CHECK_NULL);
// ClassLoader.loadClass() wants external class name format, i.e., convert '/' chars to '.'
Handle ext_class_name = java_lang_String::externalize_classname(s, CHECK_NULL);
Handle loader = Handle(THREAD, SystemDictionary::java_system_loader());

JavaCalls::call_virtual(&result,
loader, //SystemDictionary::java_system_loader(),
vmClasses::ClassLoader_klass(),
vmSymbols::loadClass_name(),
vmSymbols::string_class_signature(),
ext_class_name,
CHECK_NULL);

assert(result.get_type() == T_OBJECT, "just checking");
oop obj = result.get_oop();
assert(obj != NULL, "jdk.internal.loader.BuiltinClassLoader::loadClass never returns null");
@@ -592,12 +633,12 @@ Klass* ClassListParser::load_current_class(TRAPS) {
// If "source:" tag is specified, all super class and super interfaces must be specified in the
// class list file.
klass = load_class_from_source(class_name_symbol, CHECK_NULL);
if (HAS_PENDING_EXCEPTION) {
ArchiveUtils::check_for_oom(PENDING_EXCEPTION); // exit on OOM
}
}

if (klass != NULL && klass->is_instance_klass() && is_id_specified()) {
assert(klass != NULL, "exception should have been thrown");
assert(klass->is_instance_klass(), "array classes should have been filtered out");

if (is_id_specified()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
int id = this->id();
SystemDictionaryShared::update_shared_entry(ik, id);
@@ -116,8 +116,11 @@ class ClassListParser : public StackObj {
void print_actual_interfaces(InstanceKlass *ik);
bool is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS);

void resolve_indy(Symbol* class_name_symbol, TRAPS);
void resolve_indy(Thread* current, Symbol* class_name_symbol);
void resolve_indy_impl(Symbol* class_name_symbol, TRAPS);
bool parse_one_line();
Klass* load_current_class(Symbol* class_name_symbol, TRAPS);

public:
ClassListParser(const char* file);
~ClassListParser();
@@ -129,7 +132,7 @@ class ClassListParser : public StackObj {
return _instance;
}

bool parse_one_line();
int parse(TRAPS);
void split_tokens_by_whitespace(int offset);
int split_at_tag_from_line();
bool parse_at_tags();
@@ -169,8 +172,6 @@ class ClassListParser : public StackObj {
return _class_name;
}

Klass* load_current_class(TRAPS);

bool is_loading_from_source();

bool lambda_form_line() { return _lambda_form_line; }
@@ -1268,14 +1268,7 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
name,
loader_data,
cl_info,
THREAD);
if (HAS_PENDING_EXCEPTION) {
if (DumpSharedSpaces) {
log_error(cds)("Preload Error: Failed to load %s", class_name);
}
return NULL;
}

CHECK_NULL);
result->set_classpath_index(classpath_index);
return result;
}
@@ -260,47 +260,40 @@ InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS)
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
ResourceMark rm(THREAD);
const char* class_name = name->as_C_string();

const char* file_name = file_name_for_class_name(class_name,
name->utf8_length());
assert(file_name != NULL, "invariant");

// Lookup stream for parsing .class file
ClassFileStream* stream = NULL;
ClassPathEntry* e = find_classpath_entry_from_cache(path, CHECK_NULL);
ClassPathEntry* e = find_classpath_entry_from_cache(THREAD, path);
if (e == NULL) {
return NULL;
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}

{
PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
THREAD->as_Java_thread()->get_thread_stat()->perf_timers_addr(),
PerfClassTraceTime::CLASS_LOAD);
stream = e->open_stream(file_name, CHECK_NULL);
}

if (NULL == stream) {
log_warning(cds)("Preload Warning: Cannot find %s", class_name);
if (stream == NULL) {
// open_stream could return NULL even when no exception has be thrown (JDK-8263632).
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
return NULL;
}

assert(stream != NULL, "invariant");
stream->set_verify(true);

ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
ClassLoadInfo cl_info(protection_domain);

InstanceKlass* result = KlassFactory::create_from_stream(stream,
name,
loader_data,
cl_info,
THREAD);

if (HAS_PENDING_EXCEPTION) {
log_error(cds)("Preload Error: Failed to load %s", class_name);
return NULL;
}
return result;
InstanceKlass* k = KlassFactory::create_from_stream(stream,
name,
loader_data,
cl_info,
CHECK_NULL);
return k;
}

struct CachedClassPathEntry {
@@ -310,7 +303,7 @@ struct CachedClassPathEntry {

static GrowableArray<CachedClassPathEntry>* cached_path_entries = NULL;

ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path, TRAPS) {
ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(Thread* current, const char* path) {
// This is called from dump time so it's single threaded and there's no need for a lock.
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
if (cached_path_entries == NULL) {
@@ -336,7 +329,10 @@ ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path
}
ClassPathEntry* new_entry = NULL;

new_entry = create_class_path_entry(path, &st, false, false, false, CHECK_NULL);
ExceptionMark em(current);
Thread* THREAD = current; // For exception macros.
new_entry = create_class_path_entry(path, &st, /*throw_exception=*/false,
false, false, CATCH); // will never throw
if (new_entry == NULL) {
return NULL;
}
@@ -58,7 +58,7 @@ class ClassLoaderExt: public ClassLoader { // AllStatic
static bool _has_platform_classes;

static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS);
static ClassPathEntry* find_classpath_entry_from_cache(const char* path, TRAPS);
static ClassPathEntry* find_classpath_entry_from_cache(Thread* current, const char* path);

public:
static void process_jar_manifest(ClassPathEntry* entry, bool check_for_duplicates, TRAPS);

0 comments on commit 2b93ae0

Please sign in to comment.