| /* |
| * Copyright (c) 2015, 2019, 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 "jvm.h" |
| #include "jimage.hpp" |
| #include "classfile/classListParser.hpp" |
| #include "classfile/classLoaderExt.hpp" |
| #include "classfile/symbolTable.hpp" |
| #include "classfile/systemDictionary.hpp" |
| #include "classfile/systemDictionaryShared.hpp" |
| #include "logging/log.hpp" |
| #include "logging/logTag.hpp" |
| #include "memory/metaspaceShared.hpp" |
| #include "memory/resourceArea.hpp" |
| #include "runtime/fieldType.hpp" |
| #include "runtime/handles.inline.hpp" |
| #include "runtime/javaCalls.hpp" |
| #include "utilities/defaultStream.hpp" |
| #include "utilities/hashtable.inline.hpp" |
| #include "utilities/macros.hpp" |
| |
| ClassListParser* ClassListParser::_instance = NULL; |
| |
| ClassListParser::ClassListParser(const char* file) { |
| assert(_instance == NULL, "must be singleton"); |
| _instance = this; |
| _classlist_file = file; |
| _file = NULL; |
| _line_no = 0; |
| _interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, true); |
| |
| _file = NULL; |
| // Use os::open() because neither fopen() nor os::fopen() |
| // can handle long path name on Windows. |
| int fd = os::open(file, O_RDONLY, S_IREAD); |
| if (fd != -1) { |
| // Obtain a File* from the file descriptor so that fgets() |
| // can be used in parse_one_line() |
| _file = os::open(fd, "r"); |
| } |
| |
| if (_file == NULL) { |
| char errmsg[JVM_MAXPATHLEN]; |
| os::lasterror(errmsg, JVM_MAXPATHLEN); |
| vm_exit_during_initialization("Loading classlist failed", errmsg); |
| } |
| } |
| |
| ClassListParser::~ClassListParser() { |
| if (_file) { |
| fclose(_file); |
| } |
| _instance = NULL; |
| } |
| |
| bool ClassListParser::parse_one_line() { |
| for (;;) { |
| if (fgets(_line, sizeof(_line), _file) == NULL) { |
| return false; |
| } |
| ++ _line_no; |
| _line_len = (int)strlen(_line); |
| if (_line_len > _max_allowed_line_len) { |
| error("input line too long (must be no longer than %d chars)", _max_allowed_line_len); |
| } |
| if (*_line == '#') { // comment |
| continue; |
| } |
| break; |
| } |
| |
| _id = _unspecified; |
| _super = _unspecified; |
| _interfaces->clear(); |
| _source = NULL; |
| _interfaces_specified = false; |
| |
| { |
| int len = (int)strlen(_line); |
| int i; |
| // Replace \t\r\n with ' ' |
| for (i=0; i<len; i++) { |
| if (_line[i] == '\t' || _line[i] == '\r' || _line[i] == '\n') { |
| _line[i] = ' '; |
| } |
| } |
| |
| // Remove trailing newline/space |
| while (len > 0) { |
| if (_line[len-1] == ' ') { |
| _line[len-1] = '\0'; |
| len --; |
| } else { |
| break; |
| } |
| } |
| _line_len = len; |
| _class_name = _line; |
| } |
| |
| if ((_token = strchr(_line, ' ')) == NULL) { |
| // No optional arguments are specified. |
| return true; |
| } |
| |
| // Mark the end of the name, and go to the next input char |
| *_token++ = '\0'; |
| |
| while (*_token) { |
| skip_whitespaces(); |
| |
| if (parse_int_option("id:", &_id)) { |
| continue; |
| } else if (parse_int_option("super:", &_super)) { |
| check_already_loaded("Super class", _super); |
| continue; |
| } else if (skip_token("interfaces:")) { |
| int i; |
| while (try_parse_int(&i)) { |
| check_already_loaded("Interface", i); |
| _interfaces->append(i); |
| } |
| } else if (skip_token("source:")) { |
| skip_whitespaces(); |
| _source = _token; |
| char* s = strchr(_token, ' '); |
| if (s == NULL) { |
| break; // end of input line |
| } else { |
| *s = '\0'; // mark the end of _source |
| _token = s+1; |
| } |
| } else { |
| error("Unknown input"); |
| } |
| } |
| |
| // if src is specified |
| // id super interfaces must all be specified |
| // loader may be specified |
| // else |
| // # the class is loaded from classpath |
| // id may be specified |
| // super, interfaces, loader must not be specified |
| return true; |
| } |
| |
| void ClassListParser::skip_whitespaces() { |
| while (*_token == ' ' || *_token == '\t') { |
| _token ++; |
| } |
| } |
| |
| void ClassListParser::skip_non_whitespaces() { |
| while (*_token && *_token != ' ' && *_token != '\t') { |
| _token ++; |
| } |
| } |
| |
| void ClassListParser::parse_int(int* value) { |
| skip_whitespaces(); |
| if (sscanf(_token, "%i", value) == 1) { |
| skip_non_whitespaces(); |
| if (*value < 0) { |
| error("Error: negative integers not allowed (%d)", *value); |
| } |
| } else { |
| error("Error: expected integer"); |
| } |
| } |
| |
| bool ClassListParser::try_parse_int(int* value) { |
| skip_whitespaces(); |
| if (sscanf(_token, "%i", value) == 1) { |
| skip_non_whitespaces(); |
| return true; |
| } |
| return false; |
| } |
| |
| bool ClassListParser::skip_token(const char* option_name) { |
| size_t len = strlen(option_name); |
| if (strncmp(_token, option_name, len) == 0) { |
| _token += len; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| bool ClassListParser::parse_int_option(const char* option_name, int* value) { |
| if (skip_token(option_name)) { |
| if (*value != _unspecified) { |
| error("%s specified twice", option_name); |
| } else { |
| parse_int(value); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void ClassListParser::print_specified_interfaces() { |
| const int n = _interfaces->length(); |
| jio_fprintf(defaultStream::error_stream(), "Currently specified interfaces[%d] = {\n", n); |
| for (int i=0; i<n; i++) { |
| InstanceKlass* k = lookup_class_by_id(_interfaces->at(i)); |
| jio_fprintf(defaultStream::error_stream(), " %4d = %s\n", _interfaces->at(i), k->name()->as_klass_external_name()); |
| } |
| jio_fprintf(defaultStream::error_stream(), "}\n"); |
| } |
| |
| void ClassListParser::print_actual_interfaces(InstanceKlass *ik) { |
| int n = ik->local_interfaces()->length(); |
| jio_fprintf(defaultStream::error_stream(), "Actual interfaces[%d] = {\n", n); |
| for (int i = 0; i < n; i++) { |
| InstanceKlass* e = InstanceKlass::cast(ik->local_interfaces()->at(i)); |
| jio_fprintf(defaultStream::error_stream(), " %s\n", e->name()->as_klass_external_name()); |
| } |
| jio_fprintf(defaultStream::error_stream(), "}\n"); |
| } |
| |
| void ClassListParser::error(const char *msg, ...) { |
| va_list ap; |
| va_start(ap, msg); |
| int error_index = _token - _line; |
| if (error_index >= _line_len) { |
| error_index = _line_len - 1; |
| } |
| if (error_index < 0) { |
| error_index = 0; |
| } |
| |
| jio_fprintf(defaultStream::error_stream(), |
| "An error has occurred while processing class list file %s %d:%d.\n", |
| _classlist_file, _line_no, (error_index + 1)); |
| jio_vfprintf(defaultStream::error_stream(), msg, ap); |
| |
| if (_line_len <= 0) { |
| jio_fprintf(defaultStream::error_stream(), "\n"); |
| } else { |
| jio_fprintf(defaultStream::error_stream(), ":\n"); |
| for (int i=0; i<_line_len; i++) { |
| char c = _line[i]; |
| if (c == '\0') { |
| jio_fprintf(defaultStream::error_stream(), "%s", " "); |
| } else { |
| jio_fprintf(defaultStream::error_stream(), "%c", c); |
| } |
| } |
| jio_fprintf(defaultStream::error_stream(), "\n"); |
| for (int i=0; i<error_index; i++) { |
| jio_fprintf(defaultStream::error_stream(), "%s", " "); |
| } |
| jio_fprintf(defaultStream::error_stream(), "^\n"); |
| } |
| |
| vm_exit_during_initialization("class list format error.", NULL); |
| va_end(ap); |
| } |
| |
| // This function is used for loading classes for customized class loaders |
| // during archive dumping. |
| InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) { |
| #if !(defined(_LP64) && (defined(LINUX)|| defined(SOLARIS))) |
| // The only supported platforms are: (1) Linux/64-bit and (2) Solaris/64-bit |
| // |
| // This #if condition should be in sync with the areCustomLoadersSupportedForCDS |
| // method in test/lib/jdk/test/lib/Platform.java. |
| error("AppCDS custom class loaders not supported on this platform"); |
| #endif |
| |
| if (!is_super_specified()) { |
| error("If source location is specified, super class must be also specified"); |
| } |
| if (!is_id_specified()) { |
| error("If source location is specified, id must be also specified"); |
| } |
| InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, THREAD); |
| |
| 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; |
| } |
| |
| 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()); |
| } |
| |
| if (!SystemDictionaryShared::add_non_builtin_klass(class_name, ClassLoaderData::the_null_class_loader_data(), |
| k, THREAD)) { |
| error("Duplicated class %s", _class_name); |
| } |
| |
| // This tells JVM_FindLoadedClass to not find this class. |
| k->set_shared_classpath_index(UNREGISTERED_INDEX); |
| k->clear_class_loader_type(); |
| } |
| |
| return k; |
| } |
| |
| Klass* ClassListParser::load_current_class(TRAPS) { |
| TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name, THREAD); |
| guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol."); |
| |
| Klass *klass = NULL; |
| if (!is_loading_from_source()) { |
| // Load classes for the boot/platform/app loaders only. |
| if (is_super_specified()) { |
| error("If source location is not specified, super class must not be specified"); |
| } |
| if (are_interfaces_specified()) { |
| error("If source location is not specified, interface(s) must not be specified"); |
| } |
| |
| bool non_array = !FieldType::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_0); |
| // ClassLoader.loadClass() wants external class name format, i.e., convert '/' chars to '.' |
| Handle ext_class_name = java_lang_String::externalize_classname(s, CHECK_0); |
| Handle loader = Handle(THREAD, SystemDictionary::java_system_loader()); |
| |
| JavaCalls::call_virtual(&result, |
| loader, //SystemDictionary::java_system_loader(), |
| SystemDictionary::ClassLoader_klass(), |
| vmSymbols::loadClass_name(), |
| vmSymbols::string_class_signature(), |
| ext_class_name, |
| THREAD); |
| } else { |
| // array classes are not supported in class list. |
| THROW_NULL(vmSymbols::java_lang_ClassNotFoundException()); |
| } |
| assert(result.get_type() == T_OBJECT, "just checking"); |
| oop obj = (oop) result.get_jobject(); |
| if (!HAS_PENDING_EXCEPTION && (obj != NULL)) { |
| klass = java_lang_Class::as_Klass(obj); |
| } else { // load classes in bootclasspath/a |
| if (HAS_PENDING_EXCEPTION) { |
| CLEAR_PENDING_EXCEPTION; |
| } |
| |
| if (non_array) { |
| Klass* k = SystemDictionary::resolve_or_null(class_name_symbol, CHECK_NULL); |
| if (k != NULL) { |
| klass = k; |
| } else { |
| if (!HAS_PENDING_EXCEPTION) { |
| THROW_NULL(vmSymbols::java_lang_ClassNotFoundException()); |
| } |
| } |
| } |
| } |
| } else { |
| // 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 (klass != NULL && klass->is_instance_klass() && is_id_specified()) { |
| InstanceKlass* ik = InstanceKlass::cast(klass); |
| int id = this->id(); |
| SystemDictionaryShared::update_shared_entry(ik, id); |
| InstanceKlass* old = table()->lookup(id); |
| if (old != NULL && old != ik) { |
| error("Duplicated ID %d for class %s", id, _class_name); |
| } |
| table()->add(id, ik); |
| } |
| |
| return klass; |
| } |
| |
| bool ClassListParser::is_loading_from_source() { |
| return (_source != NULL); |
| } |
| |
| InstanceKlass* ClassListParser::lookup_class_by_id(int id) { |
| InstanceKlass* klass = table()->lookup(id); |
| if (klass == NULL) { |
| error("Class ID %d has not been defined", id); |
| } |
| return klass; |
| } |
| |
| |
| InstanceKlass* ClassListParser::lookup_super_for_current_class(Symbol* super_name) { |
| if (!is_loading_from_source()) { |
| return NULL; |
| } |
| |
| InstanceKlass* k = lookup_class_by_id(super()); |
| if (super_name != k->name()) { |
| error("The specified super class %s (id %d) does not match actual super class %s", |
| k->name()->as_klass_external_name(), super(), |
| super_name->as_klass_external_name()); |
| } |
| return k; |
| } |
| |
| InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* interface_name) { |
| if (!is_loading_from_source()) { |
| return NULL; |
| } |
| |
| const int n = _interfaces->length(); |
| if (n == 0) { |
| error("Class %s implements the interface %s, but no interface has been specified in the input line", |
| _class_name, interface_name->as_klass_external_name()); |
| ShouldNotReachHere(); |
| } |
| |
| int i; |
| for (i=0; i<n; i++) { |
| InstanceKlass* k = lookup_class_by_id(_interfaces->at(i)); |
| if (interface_name == k->name()) { |
| return k; |
| } |
| } |
| |
| // interface_name is not specified by the "interfaces:" keyword. |
| print_specified_interfaces(); |
| error("The interface %s implemented by class %s does not match any of the specified interface IDs", |
| interface_name->as_klass_external_name(), _class_name); |
| ShouldNotReachHere(); |
| return NULL; |
| } |
| |