8255489: Unify the parsing of @lambda-proxy and @lambda-form-invokers tags in a classlist

Reviewed-by: iklam, minqi
This commit is contained in:
Calvin Cheung 2020-10-30 22:01:59 +00:00
parent 0f48603394
commit 36c150b199
9 changed files with 65 additions and 34 deletions

View file

@ -115,13 +115,6 @@ bool ClassListParser::parse_one_line() {
_line_len = len; _line_len = len;
} }
// Check if the line is output TRACE_RESOLVE
if (strncmp(_line, LambdaFormInvokers::lambda_form_invoker_tag(),
strlen(LambdaFormInvokers::lambda_form_invoker_tag())) == 0) {
LambdaFormInvokers::append(os::strdup((const char*)_line, mtInternal));
continue;
}
// valid line // valid line
break; break;
} }
@ -133,6 +126,7 @@ bool ClassListParser::parse_one_line() {
_source = NULL; _source = NULL;
_interfaces_specified = false; _interfaces_specified = false;
_indy_items->clear(); _indy_items->clear();
_lambda_form_line = false;
if (_line[0] == '@') { if (_line[0] == '@') {
return parse_at_tags(); return parse_at_tags();
@ -185,8 +179,8 @@ bool ClassListParser::parse_one_line() {
return true; return true;
} }
void ClassListParser::split_tokens_by_whitespace() { void ClassListParser::split_tokens_by_whitespace(int offset) {
int start = 0; int start = offset;
int end; int end;
bool done = false; bool done = false;
while (!done) { while (!done) {
@ -203,19 +197,40 @@ void ClassListParser::split_tokens_by_whitespace() {
} }
} }
int ClassListParser::split_at_tag_from_line() {
_token = _line;
char* ptr;
if ((ptr = strchr(_line, ' ')) == NULL) {
error("Too few items following the @ tag \"%s\" line #%d", _line, _line_no);
return 0;
}
*ptr++ = '\0';
while (*ptr == ' ' || *ptr == '\t') ptr++;
return (int)(ptr - _line);
}
bool ClassListParser::parse_at_tags() { bool ClassListParser::parse_at_tags() {
assert(_line[0] == '@', "must be"); assert(_line[0] == '@', "must be");
split_tokens_by_whitespace(); int offset;
if (strcmp(_indy_items->at(0), LAMBDA_PROXY_TAG) == 0) { if ((offset = split_at_tag_from_line()) == 0) {
if (_indy_items->length() < 3) { return false;
error("Line with @ tag has too few items \"%s\" line #%d", _line, _line_no); }
if (strcmp(_token, LAMBDA_PROXY_TAG) == 0) {
split_tokens_by_whitespace(offset);
if (_indy_items->length() < 2) {
error("Line with @ tag has too few items \"%s\" line #%d", _token, _line_no);
return false; return false;
} }
// set the class name // set the class name
_class_name = _indy_items->at(1); _class_name = _indy_items->at(0);
return true;
} else if (strcmp(_token, LAMBDA_FORM_TAG) == 0) {
LambdaFormInvokers::append(os::strdup((const char*)(_line + offset), mtInternal));
_lambda_form_line = true;
return true; return true;
} else { } else {
error("Invalid @ tag at the beginning of line \"%s\" line #%d", _line, _line_no); error("Invalid @ tag at the beginning of line \"%s\" line #%d", _token, _line_no);
return false; return false;
} }
} }
@ -432,7 +447,7 @@ bool ClassListParser::is_matching_cp_entry(constantPoolHandle &pool, int cp_inde
CDSIndyInfo cii; CDSIndyInfo cii;
populate_cds_indy_info(pool, cp_index, &cii, THREAD); populate_cds_indy_info(pool, cp_index, &cii, THREAD);
GrowableArray<const char*>* items = cii.items(); GrowableArray<const char*>* items = cii.items();
int indy_info_offset = 2; int indy_info_offset = 1;
if (_indy_items->length() - indy_info_offset != items->length()) { if (_indy_items->length() - indy_info_offset != items->length()) {
return false; return false;
} }

View file

@ -30,7 +30,8 @@
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp" #include "utilities/hashtable.inline.hpp"
#define LAMBDA_PROXY_TAG "@lambda-proxy:" #define LAMBDA_PROXY_TAG "@lambda-proxy"
#define LAMBDA_FORM_TAG "@lambda-form-invoker"
class ID2KlassTable : public KVHashtable<int, InstanceKlass*, mtInternal> { class ID2KlassTable : public KVHashtable<int, InstanceKlass*, mtInternal> {
public: public:
@ -99,6 +100,7 @@ class ClassListParser : public StackObj {
GrowableArray<int>* _interfaces; GrowableArray<int>* _interfaces;
bool _interfaces_specified; bool _interfaces_specified;
const char* _source; const char* _source;
bool _lambda_form_line;
bool parse_int_option(const char* option_name, int* value); bool parse_int_option(const char* option_name, int* value);
bool parse_uint_option(const char* option_name, int* value); bool parse_uint_option(const char* option_name, int* value);
@ -120,7 +122,8 @@ public:
return _instance; return _instance;
} }
bool parse_one_line(); bool parse_one_line();
void split_tokens_by_whitespace(); void split_tokens_by_whitespace(int offset);
int split_at_tag_from_line();
bool parse_at_tags(); bool parse_at_tags();
char* _token; char* _token;
void error(const char* msg, ...); void error(const char* msg, ...);
@ -162,6 +165,8 @@ public:
bool is_loading_from_source(); bool is_loading_from_source();
bool lambda_form_line() { return _lambda_form_line; }
// Look up the super or interface of the current class being loaded // Look up the super or interface of the current class being loaded
// (in this->load_current_class()). // (in this->load_current_class()).
InstanceKlass* lookup_super_for_current_class(Symbol* super_name); InstanceKlass* lookup_super_for_current_class(Symbol* super_name);

View file

@ -66,9 +66,7 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) {
int len = _lambdaform_lines->length(); int len = _lambdaform_lines->length();
objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK); objArrayHandle list_lines = oopFactory::new_objArray_handle(SystemDictionary::String_klass(), len, CHECK);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char* record = _lambdaform_lines->at(i); Handle h_line = java_lang_String::create_from_str(_lambdaform_lines->at(i), CHECK);
record += strlen(lambda_form_invoker_tag()) + 1; // skip the @lambda_form_invoker prefix
Handle h_line = java_lang_String::create_from_str(record, CHECK);
list_lines->obj_at_put(i, h_line()); list_lines->obj_at_put(i, h_line());
} }

View file

@ -42,9 +42,5 @@ class LambdaFormInvokers : public AllStatic {
static GrowableArray<char*>* lambdaform_lines() { static GrowableArray<char*>* lambdaform_lines() {
return _lambdaform_lines; return _lambdaform_lines;
} }
static const char* lambda_form_invoker_tag() {
return "@lambda-form-invoker";
}
}; };
#endif // SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP #endif // SHARE_MEMORY_LAMBDAFORMINVOKERS_HPP

View file

@ -1096,6 +1096,9 @@ int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) {
int class_count = 0; int class_count = 0;
while (parser.parse_one_line()) { while (parser.parse_one_line()) {
if (parser.lambda_form_line()) {
continue;
}
Klass* klass = parser.load_current_class(THREAD); Klass* klass = parser.load_current_class(THREAD);
if (HAS_PENDING_EXCEPTION) { if (HAS_PENDING_EXCEPTION) {
if (klass == NULL && if (klass == NULL &&

View file

@ -25,6 +25,7 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "jvm.h" #include "jvm.h"
#include "classfile/classFileStream.hpp" #include "classfile/classFileStream.hpp"
#include "classfile/classListParser.hpp"
#include "classfile/classListWriter.hpp" #include "classfile/classListWriter.hpp"
#include "classfile/classLoader.hpp" #include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.hpp" #include "classfile/classLoaderData.hpp"
@ -32,7 +33,6 @@
#include "classfile/classLoadInfo.hpp" #include "classfile/classLoadInfo.hpp"
#include "classfile/javaAssertions.hpp" #include "classfile/javaAssertions.hpp"
#include "classfile/javaClasses.inline.hpp" #include "classfile/javaClasses.inline.hpp"
#include "classfile/lambdaFormInvokers.hpp"
#include "classfile/moduleEntry.hpp" #include "classfile/moduleEntry.hpp"
#include "classfile/modules.hpp" #include "classfile/modules.hpp"
#include "classfile/packageEntry.hpp" #include "classfile/packageEntry.hpp"
@ -3883,7 +3883,7 @@ JVM_ENTRY(void, JVM_LogLambdaFormInvoker(JNIEnv *env, jstring line))
Handle h_line (THREAD, JNIHandles::resolve_non_null(line)); Handle h_line (THREAD, JNIHandles::resolve_non_null(line));
char* c_line = java_lang_String::as_utf8_string(h_line()); char* c_line = java_lang_String::as_utf8_string(h_line());
ClassListWriter w; ClassListWriter w;
w.stream()->print_cr("%s %s", LambdaFormInvokers::lambda_form_invoker_tag(), c_line); w.stream()->print_cr("%s %s", LAMBDA_FORM_TAG, c_line);
} }
#endif // INCLUDE_CDS #endif // INCLUDE_CDS
JVM_END JVM_END

View file

@ -42,7 +42,7 @@ public class BadBSM {
OutputAnalyzer out = TestCommon.dump(appJar, OutputAnalyzer out = TestCommon.dump(appJar,
TestCommon.list("WrongBSM", TestCommon.list("WrongBSM",
"@lambda-proxy: WrongBSM 7")); "@lambda-proxy WrongBSM 7"));
out.shouldHaveExitValue(0); out.shouldHaveExitValue(0);
out.shouldContain( "is_supported_invokedynamic check failed for cp_index 7"); out.shouldContain( "is_supported_invokedynamic check failed for cp_index 7");
} }

View file

@ -105,5 +105,12 @@ public class DumpClassListWithLF extends ClassListFormatBase {
"Hello", "Hello",
"@lambda-form-invoker [SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_L L"), "@lambda-form-invoker [SPECIES_RESOLVE] java.lang.invoke.BoundMethodHandle$Species_L L"),
"Incorrect number of items in the line: 3"); "Incorrect number of items in the line: 3");
// 10. The line with incorrect (less) number of items.
dumpShouldFail(
"TESTCASE 10: With incorrect @lambda-form-invoker tag",
appJar, classlist(
"Hello",
"@lambda-form-invoker-xxx [LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeStatic"),
"Invalid @ tag at the beginning of line \"@lambda-form-invoker-xxx\" line #2");
} }
} }

View file

@ -43,35 +43,42 @@ public class LambdaProxyClasslist {
// 1. No error with a correct @lambda-proxy entry. // 1. No error with a correct @lambda-proxy entry.
OutputAnalyzer out = TestCommon.dump(appJar, OutputAnalyzer out = TestCommon.dump(appJar,
TestCommon.list("LambHello", TestCommon.list("LambHello",
"@lambda-proxy: LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V")); "@lambda-proxy LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V"));
out.shouldHaveExitValue(0); out.shouldHaveExitValue(0);
// 2. Error if the @lambda-proxy entry is too short. // 2. Error if the @lambda-proxy entry is too short.
out = TestCommon.dump(appJar, out = TestCommon.dump(appJar,
TestCommon.list("LambHello", TestCommon.list("LambHello",
"@lambda-proxy: LambHello")); "@lambda-proxy LambHello"));
out.shouldContain("An error has occurred while processing class list file") out.shouldContain("An error has occurred while processing class list file")
.shouldContain("Line with @ tag has too few items \"@lambda-proxy:\" line #2") .shouldContain("Line with @ tag has too few items \"@lambda-proxy\" line #2")
.shouldContain("class list format error") .shouldContain("class list format error")
.shouldHaveExitValue(1); .shouldHaveExitValue(1);
// 3. Warning message if there's an incorrect signature in the @lambda-proxy entry. // 3. Warning message if there's an incorrect signature in the @lambda-proxy entry.
out = TestCommon.dump(appJar, out = TestCommon.dump(appJar,
TestCommon.list("LambHello", TestCommon.list("LambHello",
"@lambda-proxy: LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()Z")); "@lambda-proxy LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()Z"));
out.shouldContain("[warning][cds] No invoke dynamic constant pool entry can be found for class LambHello. The classlist is probably out-of-date.") out.shouldContain("[warning][cds] No invoke dynamic constant pool entry can be found for class LambHello. The classlist is probably out-of-date.")
.shouldHaveExitValue(0); .shouldHaveExitValue(0);
// 4. More blank spaces in between items should be fine. // 4. More blank spaces in between items should be fine.
out = TestCommon.dump(appJar, out = TestCommon.dump(appJar,
TestCommon.list("LambHello", TestCommon.list("LambHello",
"@lambda-proxy: LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V")); "@lambda-proxy LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V"));
out.shouldHaveExitValue(0); out.shouldHaveExitValue(0);
// 5. Trailing spaces at the end of the @lambda-proxy line should be fine. // 5. Trailing spaces at the end of the @lambda-proxy line should be fine.
out = TestCommon.dump(appJar, out = TestCommon.dump(appJar,
TestCommon.list("LambHello", TestCommon.list("LambHello",
"@lambda-proxy: LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V ")); "@lambda-proxy LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V "));
out.shouldHaveExitValue(0); out.shouldHaveExitValue(0);
// 6. Error on invalid @lambda-proxy tag
out = TestCommon.dump(appJar,
TestCommon.list("LambHello",
"@lambda-proxy: LambHello run ()Ljava/lang/Runnable; ()V REF_invokeStatic LambHello lambda$doTest$0 ()V ()V"));
out.shouldContain("Invalid @ tag at the beginning of line \"@lambda-proxy:\" line #2")
.shouldHaveExitValue(1);
} }
} }