Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
8247666: Support Lambda proxy classes in static CDS archive
Reviewed-by: iklam, mchung
  • Loading branch information
calvinccheung committed Oct 19, 2020
1 parent e2e11d3 commit 74ac77e
Show file tree
Hide file tree
Showing 38 changed files with 1,960 additions and 130 deletions.
4 changes: 2 additions & 2 deletions make/hotspot/symbols/symbols-unix
Expand Up @@ -143,15 +143,15 @@ JVM_InternString
JVM_Interrupt
JVM_InvokeMethod
JVM_IsArrayClass
JVM_IsDynamicDumpingEnabled
JVM_IsSharingEnabled
JVM_IsCDSDumpingEnabled
JVM_IsConstructorIx
JVM_IsDumpingClassList
JVM_IsHiddenClass
JVM_IsInterface
JVM_IsPrimitiveClass
JVM_IsRecord
JVM_IsSameClassPackage
JVM_IsSharingEnabled
JVM_IsSupportedJNIVersion
JVM_IsThreadAlive
JVM_IsVMGeneratedMethodIx
Expand Down
188 changes: 181 additions & 7 deletions src/hotspot/share/classfile/classListParser.cpp
Expand Up @@ -32,10 +32,14 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "interpreter/bytecode.hpp"
#include "interpreter/bytecodeStream.hpp"
#include "interpreter/linkResolver.hpp"
#include "logging/log.hpp"
#include "logging/logTag.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "oops/constantPool.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
Expand Down Expand Up @@ -65,6 +69,7 @@ ClassListParser::ClassListParser(const char* file) {
}
_line_no = 0;
_interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, mtClass);
_indy_items = new (ResourceObj::C_HEAP, mtClass) GrowableArray<const char*>(9, mtClass);
}

ClassListParser::~ClassListParser() {
Expand Down Expand Up @@ -127,6 +132,11 @@ bool ClassListParser::parse_one_line() {
_interfaces->clear();
_source = NULL;
_interfaces_specified = false;
_indy_items->clear();

if (_line[0] == '@') {
return parse_at_tags();
}

if ((_token = strchr(_line, ' ')) == NULL) {
// No optional arguments are specified.
Expand All @@ -139,14 +149,14 @@ bool ClassListParser::parse_one_line() {
while (*_token) {
skip_whitespaces();

if (parse_int_option("id:", &_id)) {
if (parse_uint_option("id:", &_id)) {
continue;
} else if (parse_int_option("super:", &_super)) {
} else if (parse_uint_option("super:", &_super)) {
check_already_loaded("Super class", _super);
continue;
} else if (skip_token("interfaces:")) {
int i;
while (try_parse_int(&i)) {
while (try_parse_uint(&i)) {
check_already_loaded("Interface", i);
_interfaces->append(i);
}
Expand Down Expand Up @@ -175,6 +185,41 @@ bool ClassListParser::parse_one_line() {
return true;
}

void ClassListParser::split_tokens_by_whitespace() {
int start = 0;
int end;
bool done = false;
while (!done) {
while (_line[start] == ' ' || _line[start] == '\t') start++;
end = start;
while (_line[end] && _line[end] != ' ' && _line[end] != '\t') end++;
if (_line[end] == '\0') {
done = true;
} else {
_line[end] = '\0';
}
_indy_items->append(_line + start);
start = ++end;
}
}

bool ClassListParser::parse_at_tags() {
assert(_line[0] == '@', "must be");
split_tokens_by_whitespace();
if (strcmp(_indy_items->at(0), LAMBDA_PROXY_TAG) == 0) {
if (_indy_items->length() < 3) {
error("Line with @ tag has too few items \"%s\" line #%d", _line, _line_no);
return false;
}
// set the class name
_class_name = _indy_items->at(1);
return true;
} else {
error("Invalid @ tag at the beginning of line \"%s\" line #%d", _line, _line_no);
return false;
}
}

void ClassListParser::skip_whitespaces() {
while (*_token == ' ' || *_token == '\t') {
_token ++;
Expand All @@ -191,15 +236,19 @@ 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) {
void ClassListParser::parse_uint(int* value) {
parse_int(value);
if (*value < 0) {
error("Error: negative integers not allowed (%d)", *value);
}
}

bool ClassListParser::try_parse_uint(int* value) {
skip_whitespaces();
if (sscanf(_token, "%i", value) == 1) {
skip_non_whitespaces();
Expand Down Expand Up @@ -230,6 +279,18 @@ bool ClassListParser::parse_int_option(const char* option_name, int* value) {
return false;
}

bool ClassListParser::parse_uint_option(const char* option_name, int* value) {
if (skip_token(option_name)) {
if (*value != _unspecified) {
error("%s specified twice", option_name);
} else {
parse_uint(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);
Expand Down Expand Up @@ -336,9 +397,122 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
return k;
}

void ClassListParser::populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS) {
// Caller needs to allocate ResourceMark.
int type_index = pool->bootstrap_name_and_type_ref_index_at(cp_index);
int name_index = pool->name_ref_index_at(type_index);
cii->add_item(pool->symbol_at(name_index)->as_C_string());
int sig_index = pool->signature_ref_index_at(type_index);
cii->add_item(pool->symbol_at(sig_index)->as_C_string());
int argc = pool->bootstrap_argument_count_at(cp_index);
if (argc > 0) {
for (int arg_i = 0; arg_i < argc; arg_i++) {
int arg = pool->bootstrap_argument_index_at(cp_index, arg_i);
jbyte tag = pool->tag_at(arg).value();
if (tag == JVM_CONSTANT_MethodType) {
cii->add_item(pool->method_type_signature_at(arg)->as_C_string());
} 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());
}
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 {
ShouldNotReachHere();
}
}
}
}

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);
GrowableArray<const char*>* items = cii.items();
int indy_info_offset = 2;
if (_indy_items->length() - indy_info_offset != items->length()) {
return false;
}
for (int i = 0; i < items->length(); i++) {
if (strcmp(_indy_items->at(i + indy_info_offset), items->at(i)) != 0) {
return false;
}
}
return true;
}

void ClassListParser::resolve_indy(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, THREAD); // FIXME should really be just a lookup
if (klass != NULL && klass->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(klass);
MetaspaceShared::try_link_class(ik, THREAD);
assert(!HAS_PENDING_EXCEPTION, "unexpected exception");

ConstantPool* cp = ik->constants();
ConstantPoolCache* cpcache = cp->cache();
bool found = false;
for (int cpcindex = 0; cpcindex < cpcache->length(); cpcindex ++) {
int indy_index = ConstantPool::encode_invokedynamic_index(cpcindex);
ConstantPoolCacheEntry* cpce = cpcache->entry_at(cpcindex);
int pool_index = cpce->constant_pool_index();
constantPoolHandle pool(THREAD, cp);
if (pool->tag_at(pool_index).is_invoke_dynamic()) {
BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index);
Handle bsm = bootstrap_specifier.resolve_bsm(THREAD);
if (!SystemDictionaryShared::is_supported_invokedynamic(&bootstrap_specifier)) {
tty->print_cr("is_supported_invokedynamic check failed for cp_index %d", pool_index);
continue;
}
if (is_matching_cp_entry(pool, pool_index, THREAD)) {
found = true;
CallInfo info;
bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(info, THREAD);
if (!is_done) {
// resolve it
Handle recv;
LinkResolver::resolve_invoke(info, recv, pool, indy_index, Bytecodes::_invokedynamic, THREAD);
break;
}
cpce->set_dynamic_call(pool, info);
if (HAS_PENDING_EXCEPTION) {
ResourceMark rm(THREAD);
tty->print("resolve_indy for class %s has", class_name_symbol->as_C_string());
oop message = java_lang_Throwable::message(PENDING_EXCEPTION);
if (message != NULL) {
char* ex_msg = java_lang_String::as_utf8_string(message);
tty->print_cr(" exception pending '%s %s'",
PENDING_EXCEPTION->klass()->external_name(), ex_msg);
} else {
tty->print_cr(" exception pending %s ",
PENDING_EXCEPTION->klass()->external_name());
}
exit(1);
}
}
}
}
if (!found) {
ResourceMark rm(THREAD);
log_warning(cds)("No invoke dynamic constant pool entry can be found for class %s. The classlist is probably out-of-date.",
class_name_symbol->as_C_string());
}
}
}

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;
if (!is_loading_from_source()) {
// Load classes for the boot/platform/app loaders only.
Expand Down
44 changes: 43 additions & 1 deletion src/hotspot/share/classfile/classListParser.hpp
Expand Up @@ -30,11 +30,43 @@
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"

#define LAMBDA_PROXY_TAG "@lambda-proxy:"

class ID2KlassTable : public KVHashtable<int, InstanceKlass*, mtInternal> {
public:
ID2KlassTable() : KVHashtable<int, InstanceKlass*, mtInternal>(1987) {}
};

class CDSIndyInfo {
GrowableArray<const char*>* _items;
public:
CDSIndyInfo() : _items(NULL) {}
void add_item(const char* item) {
if (_items == NULL) {
_items = new GrowableArray<const char*>(9);
}
assert(_items != NULL, "sanity");
_items->append(item);
}
void add_ref_kind(int ref_kind) {
switch (ref_kind) {
case JVM_REF_getField : _items->append("REF_getField"); break;
case JVM_REF_getStatic : _items->append("REF_getStatic"); break;
case JVM_REF_putField : _items->append("REF_putField"); break;
case JVM_REF_putStatic : _items->append("REF_putStatic"); break;
case JVM_REF_invokeVirtual : _items->append("REF_invokeVirtual"); break;
case JVM_REF_invokeStatic : _items->append("REF_invokeStatic"); break;
case JVM_REF_invokeSpecial : _items->append("REF_invokeSpecial"); break;
case JVM_REF_newInvokeSpecial : _items->append("REF_newInvokeSpecial"); break;
case JVM_REF_invokeInterface : _items->append("REF_invokeInterface"); break;
default : ShouldNotReachHere();
}
}
GrowableArray<const char*>* items() {
return _items;
}
};

class ClassListParser : public StackObj {
enum {
_unspecified = -999,
Expand All @@ -61,20 +93,25 @@ class ClassListParser : public StackObj {
int _line_len; // Original length of the input line.
int _line_no; // Line number for current line being parsed
const char* _class_name;
GrowableArray<const char*>* _indy_items; // items related to invoke dynamic for archiving lambda proxy classes
int _id;
int _super;
GrowableArray<int>* _interfaces;
bool _interfaces_specified;
const char* _source;

bool parse_int_option(const char* option_name, int* value);
bool parse_uint_option(const char* option_name, int* value);
InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS);
ID2KlassTable *table() {
return &_id2klass_table;
}
InstanceKlass* lookup_class_by_id(int id);
void print_specified_interfaces();
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);
public:
ClassListParser(const char* file);
~ClassListParser();
Expand All @@ -83,10 +120,13 @@ class ClassListParser : public StackObj {
return _instance;
}
bool parse_one_line();
void split_tokens_by_whitespace();
bool parse_at_tags();
char* _token;
void error(const char* msg, ...);
void parse_int(int* value);
bool try_parse_int(int* value);
void parse_uint(int* value);
bool try_parse_uint(int* value);
bool skip_token(const char* option_name);
void skip_whitespaces();
void skip_non_whitespaces();
Expand Down Expand Up @@ -126,5 +166,7 @@ class ClassListParser : public StackObj {
// (in this->load_current_class()).
InstanceKlass* lookup_super_for_current_class(Symbol* super_name);
InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name);

static void populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS);
};
#endif // SHARE_CLASSFILE_CLASSLISTPARSER_HPP

1 comment on commit 74ac77e

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented on 74ac77e Oct 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.