Skip to content

Commit b818679

Browse files
committed
8293980: Resolve CONSTANT_FieldRef at CDS dump time
Reviewed-by: erikj, matsaave, heidinga
1 parent eb2488f commit b818679

35 files changed

+1119
-173
lines changed

make/GenerateLinkOptData.gmk

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,23 @@ ifeq ($(EXTERNAL_BUILDJDK), true)
6262
INTERIM_IMAGE_DIR := $(BUILD_JDK)
6363
endif
6464

65+
# These are needed for deterministic classlist:
66+
# - The classlist can be influenced by locale. Always set it to en/US.
67+
# - Run with -Xint, as the compiler can speculatively resolve constant pool entries.
68+
# - ForkJoinPool parallelism can cause constant pool resolution to be non-deterministic.
69+
CLASSLIST_FILE_VM_OPTS = \
70+
-Duser.language=en -Duser.country=US \
71+
-Xint \
72+
-Djava.util.concurrent.ForkJoinPool.common.parallelism=0
73+
6574
# Save the stderr output of the command and print it along with stdout in case
6675
# something goes wrong.
6776
$(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST_JAR)
6877
$(call MakeDir, $(LINK_OPT_DIR))
6978
$(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $@))
7079
$(call LogInfo, Generating $(patsubst $(OUTPUTDIR)/%, %, $(JLI_TRACE_FILE)))
7180
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw \
72-
-Duser.language=en -Duser.country=US \
81+
$(CLASSLIST_FILE_VM_OPTS) \
7382
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
7483
build.tools.classlist.HelloClasslist $(LOG_DEBUG)
7584
$(GREP) -v HelloClasslist $@.raw > $@.interim
@@ -79,7 +88,7 @@ $(CLASSLIST_FILE): $(INTERIM_IMAGE_DIR)/bin/java$(EXECUTABLE_SUFFIX) $(CLASSLIST
7988
$(FIXPATH) $(INTERIM_IMAGE_DIR)/bin/java -XX:DumpLoadedClassList=$@.raw.2 \
8089
-XX:SharedClassListFile=$@.interim -XX:SharedArchiveFile=$@.jsa \
8190
-Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true \
82-
-Duser.language=en -Duser.country=US \
91+
$(CLASSLIST_FILE_VM_OPTS) \
8392
--module-path $(SUPPORT_OUTPUTDIR)/classlist.jar \
8493
-cp $(SUPPORT_OUTPUTDIR)/classlist.jar \
8594
build.tools.classlist.HelloClasslist \

src/hotspot/share/cds/archiveBuilder.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,14 @@ void ArchiveBuilder::write_pointer_in_buffer(address* ptr_location, address src_
717717
}
718718
}
719719

720+
void ArchiveBuilder::mark_and_relocate_to_buffered_addr(address* ptr_location) {
721+
assert(*ptr_location != nullptr, "sanity");
722+
if (!is_in_mapped_static_archive(*ptr_location)) {
723+
*ptr_location = get_buffered_addr(*ptr_location);
724+
}
725+
ArchivePtrMarker::mark_pointer(ptr_location);
726+
}
727+
720728
address ArchiveBuilder::get_buffered_addr(address src_addr) const {
721729
SourceObjInfo* p = _src_obj_table.get(src_addr);
722730
assert(p != nullptr, "src_addr " INTPTR_FORMAT " is used but has not been archived",
@@ -755,6 +763,16 @@ void ArchiveBuilder::make_klasses_shareable() {
755763
int num_obj_array_klasses = 0;
756764
int num_type_array_klasses = 0;
757765

766+
for (int i = 0; i < klasses()->length(); i++) {
767+
// Some of the code in ConstantPool::remove_unshareable_info() requires the classes
768+
// to be in linked state, so it must be call here before the next loop, which returns
769+
// all classes to unlinked state.
770+
Klass* k = get_buffered_addr(klasses()->at(i));
771+
if (k->is_instance_klass()) {
772+
InstanceKlass::cast(k)->constants()->remove_unshareable_info();
773+
}
774+
}
775+
758776
for (int i = 0; i < klasses()->length(); i++) {
759777
const char* type;
760778
const char* unlinked = "";

src/hotspot/share/cds/archiveBuilder.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,11 @@ class ArchiveBuilder : public StackObj {
408408
write_pointer_in_buffer((address*)ptr_location, (address)src_addr);
409409
}
410410

411+
void mark_and_relocate_to_buffered_addr(address* ptr_location);
412+
template <typename T> void mark_and_relocate_to_buffered_addr(T ptr_location) {
413+
mark_and_relocate_to_buffered_addr((address*)ptr_location);
414+
}
415+
411416
address get_buffered_addr(address src_addr) const;
412417
template <typename T> T get_buffered_addr(T src_addr) const {
413418
return (T)get_buffered_addr((address)src_addr);

src/hotspot/share/cds/archiveUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ void ArchiveUtils::log_to_classlist(BootstrapInfo* bootstrap_specifier, TRAPS) {
357357
ResourceMark rm(THREAD);
358358
int pool_index = bootstrap_specifier->bss_index();
359359
ClassListWriter w;
360-
w.stream()->print("%s %s", LAMBDA_PROXY_TAG, pool->pool_holder()->name()->as_C_string());
360+
w.stream()->print("%s %s", ClassListParser::lambda_proxy_tag(), pool->pool_holder()->name()->as_C_string());
361361
CDSIndyInfo cii;
362362
ClassListParser::populate_cds_indy_info(pool, pool_index, &cii, CHECK);
363363
GrowableArray<const char*>* indy_items = cii.items();

src/hotspot/share/cds/classListParser.cpp

Lines changed: 130 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "precompiled.hpp"
2626
#include "cds/archiveUtils.hpp"
2727
#include "cds/classListParser.hpp"
28+
#include "cds/classPrelinker.hpp"
2829
#include "cds/lambdaFormInvokers.hpp"
2930
#include "cds/metaspaceShared.hpp"
3031
#include "cds/unregisteredClasses.hpp"
@@ -52,6 +53,10 @@
5253
#include "utilities/macros.hpp"
5354
#include "utilities/utf8.hpp"
5455

56+
const char* ClassListParser::CONSTANT_POOL_TAG = "@cp";
57+
const char* ClassListParser::LAMBDA_FORM_TAG = "@lambda-form-invoker";
58+
const char* ClassListParser::LAMBDA_PROXY_TAG = "@lambda-proxy";
59+
5560
volatile Thread* ClassListParser::_parsing_thread = nullptr;
5661
ClassListParser* ClassListParser::_instance = nullptr;
5762

@@ -299,6 +304,9 @@ void ClassListParser::parse_at_tags(TRAPS) {
299304
}
300305
} else if (strcmp(_token, LAMBDA_FORM_TAG) == 0) {
301306
LambdaFormInvokers::append(os::strdup((const char*)(_line + offset), mtInternal));
307+
} else if (strcmp(_token, CONSTANT_POOL_TAG) == 0) {
308+
_token = _line + offset;
309+
parse_constant_pool_tag();
302310
} else {
303311
error("Invalid @ tag at the beginning of line \"%s\" line #%zu", _token, lineno());
304312
}
@@ -395,9 +403,14 @@ void ClassListParser::print_actual_interfaces(InstanceKlass* ik) {
395403
jio_fprintf(defaultStream::error_stream(), "}\n");
396404
}
397405

398-
void ClassListParser::error(const char* msg, ...) {
406+
void ClassListParser::print_diagnostic_info(outputStream* st, const char* msg, ...) {
399407
va_list ap;
400408
va_start(ap, msg);
409+
print_diagnostic_info(st, msg, ap);
410+
va_end(ap);
411+
}
412+
413+
void ClassListParser::print_diagnostic_info(outputStream* st, const char* msg, va_list ap) {
401414
int error_index = pointer_delta_as_int(_token, _line);
402415
if (error_index >= _line_len) {
403416
error_index = _line_len - 1;
@@ -412,25 +425,34 @@ void ClassListParser::error(const char* msg, ...) {
412425
jio_vfprintf(defaultStream::error_stream(), msg, ap);
413426

414427
if (_line_len <= 0) {
415-
jio_fprintf(defaultStream::error_stream(), "\n");
428+
st->print("\n");
416429
} else {
417-
jio_fprintf(defaultStream::error_stream(), ":\n");
430+
st->print(":\n");
418431
for (int i=0; i<_line_len; i++) {
419432
char c = _line[i];
420433
if (c == '\0') {
421-
jio_fprintf(defaultStream::error_stream(), "%s", " ");
434+
st->print("%s", " ");
422435
} else {
423-
jio_fprintf(defaultStream::error_stream(), "%c", c);
436+
st->print("%c", c);
424437
}
425438
}
426-
jio_fprintf(defaultStream::error_stream(), "\n");
439+
st->print("\n");
427440
for (int i=0; i<error_index; i++) {
428-
jio_fprintf(defaultStream::error_stream(), "%s", " ");
441+
st->print("%s", " ");
429442
}
430-
jio_fprintf(defaultStream::error_stream(), "^\n");
443+
st->print("^\n");
431444
}
432-
va_end(ap);
445+
}
433446

447+
void ClassListParser::error(const char* msg, ...) {
448+
va_list ap;
449+
va_start(ap, msg);
450+
fileStream fs(defaultStream::error_stream());
451+
//TODO: we should write to UL/error instead, but that requires fixing some tests cases.
452+
//LogTarget(Error, cds) lt;
453+
//LogStream ls(lt);
454+
print_diagnostic_info(&fs, msg, ap);
455+
va_end(ap);
434456
vm_exit_during_initialization("class list format error.", nullptr);
435457
}
436458

@@ -453,6 +475,16 @@ void ClassListParser::check_class_name(const char* class_name) {
453475
}
454476
}
455477

478+
void ClassListParser::constant_pool_resolution_warning(const char* msg, ...) {
479+
va_list ap;
480+
va_start(ap, msg);
481+
LogTarget(Warning, cds, resolve) lt;
482+
LogStream ls(lt);
483+
print_diagnostic_info(&ls, msg, ap);
484+
ls.print("Your classlist may be out of sync with the JDK or the application.");
485+
va_end(ap);
486+
}
487+
456488
// This function is used for loading classes for customized class loaders
457489
// during archive dumping.
458490
InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) {
@@ -727,3 +759,92 @@ InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* inter
727759
ShouldNotReachHere();
728760
return nullptr;
729761
}
762+
763+
InstanceKlass* ClassListParser::find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop) {
764+
Handle class_loader(current, class_loader_oop);
765+
Handle protection_domain;
766+
return SystemDictionary::find_instance_klass(current, class_name_symbol, class_loader, protection_domain);
767+
}
768+
769+
InstanceKlass* ClassListParser::find_builtin_class(JavaThread* current, const char* class_name) {
770+
TempNewSymbol class_name_symbol = SymbolTable::new_symbol(class_name);
771+
InstanceKlass* ik;
772+
773+
if ( (ik = find_builtin_class_helper(current, class_name_symbol, nullptr)) != nullptr
774+
|| (ik = find_builtin_class_helper(current, class_name_symbol, SystemDictionary::java_platform_loader())) != nullptr
775+
|| (ik = find_builtin_class_helper(current, class_name_symbol, SystemDictionary::java_system_loader())) != nullptr) {
776+
return ik;
777+
} else {
778+
return nullptr;
779+
}
780+
}
781+
782+
void ClassListParser::parse_constant_pool_tag() {
783+
if (parse_lambda_forms_invokers_only()) {
784+
return;
785+
}
786+
787+
JavaThread* THREAD = JavaThread::current();
788+
skip_whitespaces();
789+
char* class_name = _token;
790+
skip_non_whitespaces();
791+
*_token = '\0';
792+
_token ++;
793+
794+
InstanceKlass* ik = find_builtin_class(THREAD, class_name);
795+
if (ik == nullptr) {
796+
_token = class_name;
797+
if (strstr(class_name, "/$Proxy") != nullptr ||
798+
strstr(class_name, "MethodHandle$Species_") != nullptr) {
799+
// ignore -- TODO: we should filter these out in classListWriter.cpp
800+
} else {
801+
constant_pool_resolution_warning("class %s is not (yet) loaded by one of the built-in loaders", class_name);
802+
}
803+
return;
804+
}
805+
806+
ResourceMark rm(THREAD);
807+
constantPoolHandle cp(THREAD, ik->constants());
808+
GrowableArray<bool> preresolve_list(cp->length(), cp->length(), false);
809+
bool preresolve_class = false;
810+
bool preresolve_fmi = false;
811+
bool preresolve_indy = false;
812+
813+
while (*_token) {
814+
int cp_index;
815+
skip_whitespaces();
816+
parse_uint(&cp_index);
817+
if (cp_index < 1 || cp_index >= cp->length()) {
818+
constant_pool_resolution_warning("Invalid constant pool index %d", cp_index);
819+
return;
820+
} else {
821+
preresolve_list.at_put(cp_index, true);
822+
}
823+
constantTag cp_tag = cp->tag_at(cp_index);
824+
switch (cp_tag.value()) {
825+
case JVM_CONSTANT_UnresolvedClass:
826+
preresolve_class = true;
827+
break;
828+
case JVM_CONSTANT_UnresolvedClassInError:
829+
case JVM_CONSTANT_Class:
830+
// ignore
831+
break;
832+
case JVM_CONSTANT_Fieldref:
833+
preresolve_fmi = true;
834+
break;
835+
break;
836+
default:
837+
constant_pool_resolution_warning("Unsupported constant pool index %d: %s (type=%d)",
838+
cp_index, cp_tag.internal_name(), cp_tag.value());
839+
return;
840+
}
841+
}
842+
843+
if (preresolve_class) {
844+
ClassPrelinker::preresolve_class_cp_entries(THREAD, ik, &preresolve_list);
845+
}
846+
if (preresolve_fmi) {
847+
ClassPrelinker::preresolve_field_and_method_cp_entries(THREAD, ik, &preresolve_list);
848+
}
849+
}
850+

src/hotspot/share/cds/classListParser.hpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@
3131
#include "utilities/istream.hpp"
3232
#include "utilities/resizeableResourceHash.hpp"
3333

34-
#define LAMBDA_PROXY_TAG "@lambda-proxy"
35-
#define LAMBDA_FORM_TAG "@lambda-form-invoker"
36-
3734
class constantPoolHandle;
3835
class Thread;
3936

@@ -68,6 +65,10 @@ class CDSIndyInfo {
6865
};
6966

7067
class ClassListParser : public StackObj {
68+
static const char* CONSTANT_POOL_TAG;
69+
static const char* LAMBDA_FORM_TAG;
70+
static const char* LAMBDA_PROXY_TAG;
71+
7172
public:
7273
enum ParseMode {
7374
_parse_all,
@@ -117,17 +118,25 @@ class ClassListParser : public StackObj {
117118
void print_actual_interfaces(InstanceKlass *ik);
118119
bool is_matching_cp_entry(const constantPoolHandle &pool, int cp_index, TRAPS);
119120

121+
InstanceKlass* find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop);
122+
InstanceKlass* find_builtin_class(JavaThread* current, const char* class_name);
123+
120124
void resolve_indy(JavaThread* current, Symbol* class_name_symbol);
121125
void resolve_indy_impl(Symbol* class_name_symbol, TRAPS);
122126
void clean_up_input_line();
123127
void read_class_name_and_attributes();
124128
void parse_class_name_and_attributes(TRAPS);
125129
Klass* load_current_class(Symbol* class_name_symbol, TRAPS);
130+
void parse_constant_pool_tag();
126131

127132
size_t lineno() { return _input_stream.lineno(); }
128133
FILE* do_open(const char* file);
129134
ClassListParser(const char* file, ParseMode _parse_mode);
130135
~ClassListParser();
136+
void print_diagnostic_info(outputStream* st, const char* msg, va_list ap) ATTRIBUTE_PRINTF(3, 0);
137+
void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0);
138+
void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
139+
void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0);
131140

132141
public:
133142
static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
@@ -141,13 +150,18 @@ class ClassListParser : public StackObj {
141150
assert(_instance != nullptr, "must be");
142151
return _instance;
143152
}
153+
static const char* lambda_proxy_tag() {
154+
return LAMBDA_PROXY_TAG;
155+
}
156+
static const char* lambda_form_tag() {
157+
return LAMBDA_FORM_TAG;
158+
}
144159

145160
void parse(TRAPS);
146161
void split_tokens_by_whitespace(int offset, GrowableArray<const char*>* items);
147162
int split_at_tag_from_line();
148163
void parse_at_tags(TRAPS);
149164
char* _token;
150-
void error(const char* msg, ...);
151165
void parse_int(int* value);
152166
void parse_uint(int* value);
153167
bool try_parse_uint(int* value);

0 commit comments

Comments
 (0)