Merge
This commit is contained in:
commit
3b27432323
20
bin/idea.sh
20
bin/idea.sh
@ -136,17 +136,33 @@ add_replacement() {
|
|||||||
eval TO$NUM_REPLACEMENTS='$2'
|
eval TO$NUM_REPLACEMENTS='$2'
|
||||||
}
|
}
|
||||||
|
|
||||||
add_replacement "###BUILD_DIR###" "`dirname $SPEC`"
|
|
||||||
add_replacement "###MODULE_NAMES###" "$MODULE_NAMES"
|
add_replacement "###MODULE_NAMES###" "$MODULE_NAMES"
|
||||||
|
SPEC_DIR=`dirname $SPEC`
|
||||||
|
if [ "x$CYGPATH" = "x" ]; then
|
||||||
|
add_replacement "###BUILD_DIR###" "$SPEC_DIR"
|
||||||
add_replacement "###JTREG_HOME###" "$JT_HOME"
|
add_replacement "###JTREG_HOME###" "$JT_HOME"
|
||||||
add_replacement "###IMAGES_DIR###" "`dirname $SPEC`/images/jdk"
|
add_replacement "###IMAGES_DIR###" "$SPEC_DIR/images/jdk"
|
||||||
add_replacement "###ROOT_DIR###" "$TOPLEVEL_DIR"
|
add_replacement "###ROOT_DIR###" "$TOPLEVEL_DIR"
|
||||||
add_replacement "###IDEA_DIR###" "$IDEA_OUTPUT"
|
add_replacement "###IDEA_DIR###" "$IDEA_OUTPUT"
|
||||||
|
else
|
||||||
|
add_replacement "###BUILD_DIR###" "`cygpath -am $SPEC_DIR`"
|
||||||
|
add_replacement "###IMAGES_DIR###" "`cygpath -am $SPEC_DIR`/images/jdk"
|
||||||
|
add_replacement "###ROOT_DIR###" "`cygpath -am $TOPLEVEL_DIR`"
|
||||||
|
add_replacement "###IDEA_DIR###" "`cygpath -am $IDEA_OUTPUT`"
|
||||||
|
if [ "x$JT_HOME" = "x" ]; then
|
||||||
|
add_replacement "###JTREG_HOME###" ""
|
||||||
|
else
|
||||||
|
add_replacement "###JTREG_HOME###" "`cygpath -am $JT_HOME`"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
SOURCE_PREFIX="<sourceFolder url=\"file://"
|
SOURCE_PREFIX="<sourceFolder url=\"file://"
|
||||||
SOURCE_POSTFIX="\" isTestSource=\"false\" />"
|
SOURCE_POSTFIX="\" isTestSource=\"false\" />"
|
||||||
|
|
||||||
for root in $MODULE_ROOTS; do
|
for root in $MODULE_ROOTS; do
|
||||||
|
if [ "x$CYGPATH" != "x" ]; then
|
||||||
|
root=`cygpath -am $root`
|
||||||
|
fi
|
||||||
SOURCES=$SOURCES" $SOURCE_PREFIX""$root""$SOURCE_POSTFIX"
|
SOURCES=$SOURCES" $SOURCE_PREFIX""$root""$SOURCE_POSTFIX"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ DEFAULT_VERSION_EXTRA3=0
|
|||||||
DEFAULT_VERSION_DATE=2019-03-19
|
DEFAULT_VERSION_DATE=2019-03-19
|
||||||
DEFAULT_VERSION_CLASSFILE_MAJOR=56 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
DEFAULT_VERSION_CLASSFILE_MAJOR=56 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||||
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
||||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="10 11 12"
|
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="11 12"
|
||||||
|
|
||||||
LAUNCHER_NAME=openjdk
|
LAUNCHER_NAME=openjdk
|
||||||
PRODUCT_NAME=OpenJDK
|
PRODUCT_NAME=OpenJDK
|
||||||
|
@ -361,7 +361,7 @@ var getJibProfilesCommon = function (input, data) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
common.boot_jdk_version = "10";
|
common.boot_jdk_version = "11";
|
||||||
common.boot_jdk_home = input.get("boot_jdk", "home_path") + "/jdk-"
|
common.boot_jdk_home = input.get("boot_jdk", "home_path") + "/jdk-"
|
||||||
+ common.boot_jdk_version
|
+ common.boot_jdk_version
|
||||||
+ (input.build_os == "macosx" ? ".jdk/Contents/Home" : "");
|
+ (input.build_os == "macosx" ? ".jdk/Contents/Home" : "");
|
||||||
@ -851,9 +851,10 @@ var getJibProfilesDependencies = function (input, common) {
|
|||||||
server: "jpg",
|
server: "jpg",
|
||||||
product: "jdk",
|
product: "jdk",
|
||||||
version: common.boot_jdk_version,
|
version: common.boot_jdk_version,
|
||||||
build_number: "46",
|
build_number: "28",
|
||||||
file: "bundles/" + boot_jdk_platform + "/jdk-" + common.boot_jdk_version + "_"
|
file: "bundles/" + boot_jdk_platform + "/jdk-" + common.boot_jdk_version + "_"
|
||||||
+ boot_jdk_platform + "_bin.tar.gz",
|
+ boot_jdk_platform + "_bin"
|
||||||
|
+ (input.build_os == "windows" ? ".zip" : ".tar.gz"),
|
||||||
configure_args: "--with-boot-jdk=" + common.boot_jdk_home,
|
configure_args: "--with-boot-jdk=" + common.boot_jdk_home,
|
||||||
environment_path: common.boot_jdk_home + "/bin"
|
environment_path: common.boot_jdk_home + "/bin"
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -43,7 +43,7 @@ public class Main {
|
|||||||
/**
|
/**
|
||||||
* ASM version to be used by nasgen tool.
|
* ASM version to be used by nasgen tool.
|
||||||
*/
|
*/
|
||||||
public static final int ASM_VERSION = Opcodes.ASM5;
|
public static final int ASM_VERSION = Opcodes.ASM7;
|
||||||
|
|
||||||
private static final boolean DEBUG = Boolean.getBoolean("nasgen.debug");
|
private static final boolean DEBUG = Boolean.getBoolean("nasgen.debug");
|
||||||
|
|
||||||
|
@ -1902,8 +1902,8 @@ void TemplateInterpreterGenerator::generate_throw_exception() {
|
|||||||
Label L_done;
|
Label L_done;
|
||||||
|
|
||||||
__ ldrb(rscratch1, Address(rbcp, 0));
|
__ ldrb(rscratch1, Address(rbcp, 0));
|
||||||
__ cmpw(r1, Bytecodes::_invokestatic);
|
__ cmpw(rscratch1, Bytecodes::_invokestatic);
|
||||||
__ br(Assembler::EQ, L_done);
|
__ br(Assembler::NE, L_done);
|
||||||
|
|
||||||
// The member name argument must be restored if _invokestatic is re-executed after a PopFrame call.
|
// The member name argument must be restored if _invokestatic is re-executed after a PopFrame call.
|
||||||
// Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL.
|
// Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL.
|
||||||
@ -1938,7 +1938,6 @@ void TemplateInterpreterGenerator::generate_throw_exception() {
|
|||||||
// remove the activation (without doing throws on illegalMonitorExceptions)
|
// remove the activation (without doing throws on illegalMonitorExceptions)
|
||||||
__ remove_activation(vtos, false, true, false);
|
__ remove_activation(vtos, false, true, false);
|
||||||
// restore exception
|
// restore exception
|
||||||
// restore exception
|
|
||||||
__ get_vm_result(r0, rthread);
|
__ get_vm_result(r0, rthread);
|
||||||
|
|
||||||
// In between activations - previous activation type unknown yet
|
// In between activations - previous activation type unknown yet
|
||||||
@ -1947,9 +1946,8 @@ void TemplateInterpreterGenerator::generate_throw_exception() {
|
|||||||
//
|
//
|
||||||
// r0: exception
|
// r0: exception
|
||||||
// lr: return address/pc that threw exception
|
// lr: return address/pc that threw exception
|
||||||
// rsp: expression stack of caller
|
// esp: expression stack of caller
|
||||||
// rfp: fp of caller
|
// rfp: fp of caller
|
||||||
// FIXME: There's no point saving LR here because VM calls don't trash it
|
|
||||||
__ stp(r0, lr, Address(__ pre(sp, -2 * wordSize))); // save exception & return address
|
__ stp(r0, lr, Address(__ pre(sp, -2 * wordSize))); // save exception & return address
|
||||||
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
|
__ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
|
||||||
SharedRuntime::exception_handler_for_return_address),
|
SharedRuntime::exception_handler_for_return_address),
|
||||||
|
@ -498,7 +498,7 @@ class Assembler : public AbstractAssembler {
|
|||||||
void dmb(DMB_Opt opt, Register reg) {
|
void dmb(DMB_Opt opt, Register reg) {
|
||||||
if (VM_Version::arm_arch() >= 7) {
|
if (VM_Version::arm_arch() >= 7) {
|
||||||
emit_int32(0xF57FF050 | opt);
|
emit_int32(0xF57FF050 | opt);
|
||||||
} else {
|
} else if (VM_Version::arm_arch() == 6) {
|
||||||
bool preserve_tmp = (reg == noreg);
|
bool preserve_tmp = (reg == noreg);
|
||||||
if(preserve_tmp) {
|
if(preserve_tmp) {
|
||||||
reg = Rtemp;
|
reg = Rtemp;
|
||||||
|
@ -41,10 +41,6 @@ inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
|||||||
return strncmp(s1, s2, num);
|
return strncmp(s1, s2, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool os::obsolete_option(const JavaVMOption *option) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool os::uses_stack_guard_pages() {
|
inline bool os::uses_stack_guard_pages() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,6 @@ inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
|||||||
return strncmp(s1, s2, num);
|
return strncmp(s1, s2, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool os::obsolete_option(const JavaVMOption *option) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool os::uses_stack_guard_pages() {
|
inline bool os::uses_stack_guard_pages() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -39,10 +39,6 @@ inline int os::file_name_strncmp(const char* s1, const char* s2, size_t num) {
|
|||||||
return strncmp(s1, s2, num);
|
return strncmp(s1, s2, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool os::obsolete_option(const JavaVMOption *option) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool os::uses_stack_guard_pages() {
|
inline bool os::uses_stack_guard_pages() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -700,19 +700,6 @@ void os::breakpoint() {
|
|||||||
BREAKPOINT;
|
BREAKPOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os::obsolete_option(const JavaVMOption *option) {
|
|
||||||
if (!strncmp(option->optionString, "-Xt", 3)) {
|
|
||||||
return true;
|
|
||||||
} else if (!strncmp(option->optionString, "-Xtm", 4)) {
|
|
||||||
return true;
|
|
||||||
} else if (!strncmp(option->optionString, "-Xverifyheap", 12)) {
|
|
||||||
return true;
|
|
||||||
} else if (!strncmp(option->optionString, "-Xmaxjitcodesize", 16)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os::Solaris::valid_stack_address(Thread* thread, address sp) {
|
bool os::Solaris::valid_stack_address(Thread* thread, address sp) {
|
||||||
address stackStart = (address)thread->stack_base();
|
address stackStart = (address)thread->stack_base();
|
||||||
address stackEnd = (address)(stackStart - (address)thread->stack_size());
|
address stackEnd = (address)(stackStart - (address)thread->stack_size());
|
||||||
|
@ -45,10 +45,6 @@ inline void* os::dll_lookup(void *lib, const char *name) {
|
|||||||
return (void*)::GetProcAddress((HMODULE)lib, name);
|
return (void*)::GetProcAddress((HMODULE)lib, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool os::obsolete_option(const JavaVMOption *option) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool os::uses_stack_guard_pages() {
|
inline bool os::uses_stack_guard_pages() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ inline static void dmb_sy() {
|
|||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
".word 0xF57FF050 | 0xf" : : : "memory");
|
".word 0xF57FF050 | 0xf" : : : "memory");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else if (VM_Version::arm_arch() == 6) {
|
||||||
intptr_t zero = 0;
|
intptr_t zero = 0;
|
||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
"mcr p15, 0, %0, c7, c10, 5"
|
"mcr p15, 0, %0, c7, c10, 5"
|
||||||
@ -80,7 +80,7 @@ inline static void dmb_st() {
|
|||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
".word 0xF57FF050 | 0xe" : : : "memory");
|
".word 0xF57FF050 | 0xe" : : : "memory");
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else if (VM_Version::arm_arch() == 6) {
|
||||||
intptr_t zero = 0;
|
intptr_t zero = 0;
|
||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
"mcr p15, 0, %0, c7, c10, 5"
|
"mcr p15, 0, %0, c7, c10, 5"
|
||||||
|
@ -287,7 +287,7 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
|
|||||||
if (!is_id_specified()) {
|
if (!is_id_specified()) {
|
||||||
error("If source location is specified, id must be also specified");
|
error("If source location is specified, id must be also specified");
|
||||||
}
|
}
|
||||||
InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, THREAD);
|
InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, CHECK_NULL);
|
||||||
|
|
||||||
if (strncmp(_class_name, "java/", 5) == 0) {
|
if (strncmp(_class_name, "java/", 5) == 0) {
|
||||||
log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s",
|
log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s",
|
||||||
@ -303,8 +303,9 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
|
|||||||
_interfaces->length(), k->local_interfaces()->length());
|
_interfaces->length(), k->local_interfaces()->length());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SystemDictionaryShared::add_non_builtin_klass(class_name, ClassLoaderData::the_null_class_loader_data(),
|
bool added = SystemDictionaryShared::add_unregistered_class(k, CHECK_NULL);
|
||||||
k, THREAD)) {
|
if (!added) {
|
||||||
|
// We allow only a single unregistered class for each unique name.
|
||||||
error("Duplicated class %s", _class_name);
|
error("Duplicated class %s", _class_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +354,7 @@ Klass* ClassListParser::load_current_class(TRAPS) {
|
|||||||
vmSymbols::loadClass_name(),
|
vmSymbols::loadClass_name(),
|
||||||
vmSymbols::string_class_signature(),
|
vmSymbols::string_class_signature(),
|
||||||
ext_class_name,
|
ext_class_name,
|
||||||
THREAD);
|
THREAD); // <-- failure is handled below
|
||||||
} else {
|
} else {
|
||||||
// array classes are not supported in class list.
|
// array classes are not supported in class list.
|
||||||
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
|
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
|
||||||
|
@ -496,11 +496,11 @@ bool ClassLoaderDataGraph::is_valid(ClassLoaderData* loader_data) {
|
|||||||
|
|
||||||
// Move class loader data from main list to the unloaded list for unloading
|
// Move class loader data from main list to the unloaded list for unloading
|
||||||
// and deallocation later.
|
// and deallocation later.
|
||||||
bool ClassLoaderDataGraph::do_unloading(bool do_cleaning) {
|
bool ClassLoaderDataGraph::do_unloading() {
|
||||||
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
|
||||||
|
|
||||||
// Indicate whether safepoint cleanup is needed.
|
// Indicate whether safepoint cleanup is needed.
|
||||||
_safepoint_cleanup_needed |= do_cleaning;
|
_safepoint_cleanup_needed = true;
|
||||||
|
|
||||||
ClassLoaderData* data = _head;
|
ClassLoaderData* data = _head;
|
||||||
ClassLoaderData* prev = NULL;
|
ClassLoaderData* prev = NULL;
|
||||||
|
@ -88,7 +88,7 @@ class ClassLoaderDataGraph : public AllStatic {
|
|||||||
static void loaded_classes_do(KlassClosure* klass_closure);
|
static void loaded_classes_do(KlassClosure* klass_closure);
|
||||||
static void unlocked_loaded_classes_do(KlassClosure* klass_closure);
|
static void unlocked_loaded_classes_do(KlassClosure* klass_closure);
|
||||||
static void classes_unloading_do(void f(Klass* const));
|
static void classes_unloading_do(void f(Klass* const));
|
||||||
static bool do_unloading(bool do_cleaning);
|
static bool do_unloading();
|
||||||
|
|
||||||
// Expose state to avoid logging overhead in safepoint cleanup tasks.
|
// Expose state to avoid logging overhead in safepoint cleanup tasks.
|
||||||
static inline bool should_clean_metaspaces_and_reset();
|
static inline bool should_clean_metaspaces_and_reset();
|
||||||
|
@ -350,7 +350,3 @@ ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path
|
|||||||
cached_path_entries->insert_before(0, ccpe);
|
cached_path_entries->insert_before(0, ccpe);
|
||||||
return new_entry;
|
return new_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
Klass* ClassLoaderExt::load_one_class(ClassListParser* parser, TRAPS) {
|
|
||||||
return parser->load_current_class(THREAD);
|
|
||||||
}
|
|
||||||
|
@ -115,7 +115,6 @@ public:
|
|||||||
static void record_result(const s2 classpath_index,
|
static void record_result(const s2 classpath_index,
|
||||||
InstanceKlass* result, TRAPS);
|
InstanceKlass* result, TRAPS);
|
||||||
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
|
static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
|
||||||
static Klass* load_one_class(ClassListParser* parser, TRAPS);
|
|
||||||
static void set_has_app_classes() {
|
static void set_has_app_classes() {
|
||||||
_has_app_classes = true;
|
_has_app_classes = true;
|
||||||
}
|
}
|
||||||
|
@ -163,6 +163,7 @@ void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table
|
|||||||
msg.info("Average bucket size : %9.3f", summary.avg());
|
msg.info("Average bucket size : %9.3f", summary.avg());
|
||||||
msg.info("Variance of bucket size : %9.3f", summary.variance());
|
msg.info("Variance of bucket size : %9.3f", summary.variance());
|
||||||
msg.info("Std. dev. of bucket size: %9.3f", summary.sd());
|
msg.info("Std. dev. of bucket size: %9.3f", summary.sd());
|
||||||
|
msg.info("Maximum bucket size : %9d", (int)summary.maximum());
|
||||||
msg.info("Empty buckets : %9d", _num_empty_buckets);
|
msg.info("Empty buckets : %9d", _num_empty_buckets);
|
||||||
msg.info("Value_Only buckets : %9d", _num_value_only_buckets);
|
msg.info("Value_Only buckets : %9d", _num_value_only_buckets);
|
||||||
msg.info("Other buckets : %9d", _num_other_buckets);
|
msg.info("Other buckets : %9d", _num_other_buckets);
|
||||||
|
@ -244,8 +244,6 @@ class CompactHashtable : public SimpleCompactHashtable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CompactHashtable() : SimpleCompactHashtable() {}
|
|
||||||
|
|
||||||
// Lookup a value V from the compact table using key K
|
// Lookup a value V from the compact table using key K
|
||||||
inline V lookup(K key, unsigned int hash, int len) const {
|
inline V lookup(K key, unsigned int hash, int len) const {
|
||||||
if (_entry_count > 0) {
|
if (_entry_count > 0) {
|
||||||
@ -299,8 +297,53 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void print_table_statistics(outputStream* st, const char* name) {
|
||||||
|
st->print_cr("%s statistics:", name);
|
||||||
|
int total_entries = 0;
|
||||||
|
int max_bucket = 0;
|
||||||
|
for (u4 i = 0; i < _bucket_count; i++) {
|
||||||
|
u4 bucket_info = _buckets[i];
|
||||||
|
int bucket_type = BUCKET_TYPE(bucket_info);
|
||||||
|
int bucket_size;
|
||||||
|
|
||||||
|
if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
|
||||||
|
bucket_size = 1;
|
||||||
|
} else {
|
||||||
|
bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2;
|
||||||
|
}
|
||||||
|
total_entries += bucket_size;
|
||||||
|
if (max_bucket < bucket_size) {
|
||||||
|
max_bucket = bucket_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st->print_cr("Number of buckets : %9d", _bucket_count);
|
||||||
|
st->print_cr("Number of entries : %9d", total_entries);
|
||||||
|
st->print_cr("Maximum bucket size : %9d", max_bucket);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// OffsetCompactHashtable -- This is used to store many types of objects
|
||||||
|
// in the CDS archive. On 64-bit platforms, we save space by using a 32-bit
|
||||||
|
// offset from the CDS base address.
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
V read_value_from_compact_hashtable(address base_address, u4 offset) {
|
||||||
|
return (V)(base_address + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename K,
|
||||||
|
typename V,
|
||||||
|
bool (*EQUALS)(V value, K key, int len)
|
||||||
|
>
|
||||||
|
class OffsetCompactHashtable : public CompactHashtable<
|
||||||
|
K, V, read_value_from_compact_hashtable<V>, EQUALS> {
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Read/Write the contents of a hashtable textual dump (created by
|
// Read/Write the contents of a hashtable textual dump (created by
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include "classfile/dictionary.inline.hpp"
|
#include "classfile/dictionary.inline.hpp"
|
||||||
#include "classfile/protectionDomainCache.hpp"
|
#include "classfile/protectionDomainCache.hpp"
|
||||||
#include "classfile/systemDictionary.hpp"
|
#include "classfile/systemDictionary.hpp"
|
||||||
#include "classfile/systemDictionaryShared.hpp"
|
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "logging/logStream.hpp"
|
#include "logging/logStream.hpp"
|
||||||
#include "memory/iterator.hpp"
|
#include "memory/iterator.hpp"
|
||||||
@ -44,16 +43,8 @@
|
|||||||
// needs resizing, which is costly to do at Safepoint.
|
// needs resizing, which is costly to do at Safepoint.
|
||||||
bool Dictionary::_some_dictionary_needs_resizing = false;
|
bool Dictionary::_some_dictionary_needs_resizing = false;
|
||||||
|
|
||||||
size_t Dictionary::entry_size() {
|
|
||||||
if (DumpSharedSpaces) {
|
|
||||||
return SystemDictionaryShared::dictionary_entry_size();
|
|
||||||
} else {
|
|
||||||
return sizeof(DictionaryEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable)
|
Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable)
|
||||||
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size()),
|
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)sizeof(DictionaryEntry)),
|
||||||
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,7 +52,7 @@ Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size, bool resiza
|
|||||||
Dictionary::Dictionary(ClassLoaderData* loader_data,
|
Dictionary::Dictionary(ClassLoaderData* loader_data,
|
||||||
int table_size, HashtableBucket<mtClass>* t,
|
int table_size, HashtableBucket<mtClass>* t,
|
||||||
int number_of_entries, bool resizable)
|
int number_of_entries, bool resizable)
|
||||||
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size(), t, number_of_entries),
|
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)sizeof(DictionaryEntry), t, number_of_entries),
|
||||||
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,9 +74,6 @@ DictionaryEntry* Dictionary::new_entry(unsigned int hash, InstanceKlass* klass)
|
|||||||
DictionaryEntry* entry = (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::allocate_new_entry(hash, klass);
|
DictionaryEntry* entry = (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::allocate_new_entry(hash, klass);
|
||||||
entry->set_pd_set(NULL);
|
entry->set_pd_set(NULL);
|
||||||
assert(klass->is_instance_klass(), "Must be");
|
assert(klass->is_instance_klass(), "Must be");
|
||||||
if (DumpSharedSpaces) {
|
|
||||||
SystemDictionaryShared::init_shared_dictionary_entry(klass, entry);
|
|
||||||
}
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,26 +268,6 @@ void Dictionary::do_unloading() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dictionary::remove_classes_in_error_state() {
|
|
||||||
assert(DumpSharedSpaces, "supported only when dumping");
|
|
||||||
DictionaryEntry* probe = NULL;
|
|
||||||
for (int index = 0; index < table_size(); index++) {
|
|
||||||
for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) {
|
|
||||||
probe = *p;
|
|
||||||
InstanceKlass* ik = probe->instance_klass();
|
|
||||||
if (ik->is_in_error_state()) { // purge this entry
|
|
||||||
*p = probe->next();
|
|
||||||
free_entry(probe);
|
|
||||||
ResourceMark rm;
|
|
||||||
tty->print_cr("Preload Warning: Removed error class: %s", ik->external_name());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
p = probe->next_addr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just the classes from defining class loaders
|
// Just the classes from defining class loaders
|
||||||
void Dictionary::classes_do(void f(InstanceKlass*)) {
|
void Dictionary::classes_do(void f(InstanceKlass*)) {
|
||||||
for (int index = 0; index < table_size(); index++) {
|
for (int index = 0; index < table_size(); index++) {
|
||||||
@ -349,7 +317,6 @@ void Dictionary::classes_do(MetaspaceClosure* it) {
|
|||||||
probe != NULL;
|
probe != NULL;
|
||||||
probe = probe->next()) {
|
probe = probe->next()) {
|
||||||
it->push(probe->klass_addr());
|
it->push(probe->klass_addr());
|
||||||
((SharedDictionaryEntry*)probe)->metaspace_pointers_do(it);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,11 +357,9 @@ DictionaryEntry* Dictionary::get_entry(int index, unsigned int hash,
|
|||||||
entry != NULL;
|
entry != NULL;
|
||||||
entry = entry->next()) {
|
entry = entry->next()) {
|
||||||
if (entry->hash() == hash && entry->equals(class_name)) {
|
if (entry->hash() == hash && entry->equals(class_name)) {
|
||||||
if (!DumpSharedSpaces || SystemDictionaryShared::is_builtin(entry)) {
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,18 +388,6 @@ InstanceKlass* Dictionary::find_class(int index, unsigned int hash,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Variant of find_class for shared classes. No locking required, as
|
|
||||||
// that table is static.
|
|
||||||
|
|
||||||
InstanceKlass* Dictionary::find_shared_class(int index, unsigned int hash,
|
|
||||||
Symbol* name) {
|
|
||||||
assert (index == index_for(name), "incorrect index?");
|
|
||||||
|
|
||||||
DictionaryEntry* entry = get_entry(index, hash, name);
|
|
||||||
return (entry != NULL) ? entry->instance_klass() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Dictionary::add_protection_domain(int index, unsigned int hash,
|
void Dictionary::add_protection_domain(int index, unsigned int hash,
|
||||||
InstanceKlass* klass,
|
InstanceKlass* klass,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
@ -465,70 +418,6 @@ bool Dictionary::is_valid_protection_domain(unsigned int hash,
|
|||||||
return entry->is_valid_protection_domain(protection_domain);
|
return entry->is_valid_protection_domain(protection_domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
|
||||||
static bool is_jfr_event_class(Klass *k) {
|
|
||||||
while (k) {
|
|
||||||
if (k->name()->equals("jdk/internal/event/Event")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
k = k->super();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dictionary::reorder_dictionary_for_sharing() {
|
|
||||||
|
|
||||||
// Copy all the dictionary entries into a single master list.
|
|
||||||
assert(DumpSharedSpaces, "Should only be used at dump time");
|
|
||||||
|
|
||||||
DictionaryEntry* master_list = NULL;
|
|
||||||
for (int i = 0; i < table_size(); ++i) {
|
|
||||||
DictionaryEntry* p = bucket(i);
|
|
||||||
while (p != NULL) {
|
|
||||||
DictionaryEntry* next = p->next();
|
|
||||||
InstanceKlass*ik = p->instance_klass();
|
|
||||||
if (ik->has_signer_and_not_archived()) {
|
|
||||||
// We cannot include signed classes in the archive because the certificates
|
|
||||||
// used during dump time may be different than those used during
|
|
||||||
// runtime (due to expiration, etc).
|
|
||||||
ResourceMark rm;
|
|
||||||
tty->print_cr("Preload Warning: Skipping %s from signed JAR",
|
|
||||||
ik->name()->as_C_string());
|
|
||||||
free_entry(p);
|
|
||||||
} else if (is_jfr_event_class(ik)) {
|
|
||||||
// We cannot include JFR event classes because they need runtime-specific
|
|
||||||
// instrumentation in order to work with -XX:FlightRecorderOptions=retransform=false.
|
|
||||||
// There are only a small number of these classes, so it's not worthwhile to
|
|
||||||
// support them and make CDS more complicated.
|
|
||||||
ResourceMark rm;
|
|
||||||
tty->print_cr("Skipping JFR event class %s", ik->name()->as_C_string());
|
|
||||||
free_entry(p);
|
|
||||||
} else {
|
|
||||||
p->set_next(master_list);
|
|
||||||
master_list = p;
|
|
||||||
}
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
set_entry(i, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the dictionary entries back to the list in the correct buckets.
|
|
||||||
while (master_list != NULL) {
|
|
||||||
DictionaryEntry* p = master_list;
|
|
||||||
master_list = master_list->next();
|
|
||||||
p->set_next(NULL);
|
|
||||||
Symbol* class_name = p->instance_klass()->name();
|
|
||||||
// Since the null class loader data isn't copied to the CDS archive,
|
|
||||||
// compute the hash with NULL for loader data.
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
int index = hash_to_index(hash);
|
|
||||||
p->set_hash(hash);
|
|
||||||
p->set_next(bucket(index));
|
|
||||||
set_entry(index, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SymbolPropertyTable::SymbolPropertyTable(int table_size)
|
SymbolPropertyTable::SymbolPropertyTable(int table_size)
|
||||||
: Hashtable<Symbol*, mtSymbol>(table_size, sizeof(SymbolPropertyEntry))
|
: Hashtable<Symbol*, mtSymbol>(table_size, sizeof(SymbolPropertyEntry))
|
||||||
{
|
{
|
||||||
@ -605,10 +494,7 @@ void Dictionary::print_on(outputStream* st) const {
|
|||||||
(loader_data() == e->class_loader_data());
|
(loader_data() == e->class_loader_data());
|
||||||
st->print("%4d: %s%s", index, is_defining_class ? " " : "^", e->external_name());
|
st->print("%4d: %s%s", index, is_defining_class ? " " : "^", e->external_name());
|
||||||
ClassLoaderData* cld = e->class_loader_data();
|
ClassLoaderData* cld = e->class_loader_data();
|
||||||
if (cld == NULL) {
|
if (!loader_data()->is_the_null_class_loader_data()) {
|
||||||
// Shared class not restored yet in shared dictionary
|
|
||||||
st->print(", loader data <shared, not restored>");
|
|
||||||
} else if (!loader_data()->is_the_null_class_loader_data()) {
|
|
||||||
// Class loader output for the dictionary for the null class loader data is
|
// Class loader output for the dictionary for the null class loader data is
|
||||||
// redundant and obvious.
|
// redundant and obvious.
|
||||||
st->print(", ");
|
st->print(", ");
|
||||||
@ -634,7 +520,7 @@ void Dictionary::verify() {
|
|||||||
ClassLoaderData* cld = loader_data();
|
ClassLoaderData* cld = loader_data();
|
||||||
// class loader must be present; a null class loader is the
|
// class loader must be present; a null class loader is the
|
||||||
// boostrap loader
|
// boostrap loader
|
||||||
guarantee(cld != NULL || DumpSharedSpaces ||
|
guarantee(cld != NULL ||
|
||||||
cld->class_loader() == NULL ||
|
cld->class_loader() == NULL ||
|
||||||
cld->class_loader()->is_instance(),
|
cld->class_loader()->is_instance(),
|
||||||
"checking type of class_loader");
|
"checking type of class_loader");
|
||||||
|
@ -36,8 +36,7 @@ class DictionaryEntry;
|
|||||||
class BoolObjectClosure;
|
class BoolObjectClosure;
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// The data structure for the class loader data dictionaries (and the shared system
|
// The data structure for the class loader data dictionaries.
|
||||||
// dictionary).
|
|
||||||
|
|
||||||
class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
|
class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
|
||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
@ -54,8 +53,6 @@ class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
|
|||||||
|
|
||||||
void clean_cached_protection_domains(DictionaryEntry* probe);
|
void clean_cached_protection_domains(DictionaryEntry* probe);
|
||||||
|
|
||||||
protected:
|
|
||||||
static size_t entry_size();
|
|
||||||
public:
|
public:
|
||||||
Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable = false);
|
Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable = false);
|
||||||
Dictionary(ClassLoaderData* loader_data, int table_size, HashtableBucket<mtClass>* t, int number_of_entries, bool resizable = false);
|
Dictionary(ClassLoaderData* loader_data, int table_size, HashtableBucket<mtClass>* t, int number_of_entries, bool resizable = false);
|
||||||
@ -70,15 +67,12 @@ public:
|
|||||||
|
|
||||||
InstanceKlass* find_class(int index, unsigned int hash, Symbol* name);
|
InstanceKlass* find_class(int index, unsigned int hash, Symbol* name);
|
||||||
|
|
||||||
InstanceKlass* find_shared_class(int index, unsigned int hash, Symbol* name);
|
|
||||||
|
|
||||||
void classes_do(void f(InstanceKlass*));
|
void classes_do(void f(InstanceKlass*));
|
||||||
void classes_do(void f(InstanceKlass*, TRAPS), TRAPS);
|
void classes_do(void f(InstanceKlass*, TRAPS), TRAPS);
|
||||||
void all_entries_do(KlassClosure* closure);
|
void all_entries_do(KlassClosure* closure);
|
||||||
void classes_do(MetaspaceClosure* it);
|
void classes_do(MetaspaceClosure* it);
|
||||||
|
|
||||||
void unlink();
|
void unlink();
|
||||||
void remove_classes_in_error_state();
|
|
||||||
|
|
||||||
// Unload classes whose defining loaders are unloaded
|
// Unload classes whose defining loaders are unloaded
|
||||||
void do_unloading();
|
void do_unloading();
|
||||||
@ -92,9 +86,6 @@ public:
|
|||||||
InstanceKlass* klass,
|
InstanceKlass* klass,
|
||||||
Handle protection_domain, TRAPS);
|
Handle protection_domain, TRAPS);
|
||||||
|
|
||||||
// Sharing support
|
|
||||||
void reorder_dictionary_for_sharing() NOT_CDS_RETURN;
|
|
||||||
|
|
||||||
void print_on(outputStream* st) const;
|
void print_on(outputStream* st) const;
|
||||||
void verify();
|
void verify();
|
||||||
DictionaryEntry* bucket(int i) const {
|
DictionaryEntry* bucket(int i) const {
|
||||||
|
@ -1090,8 +1090,7 @@ oop java_lang_Class::archive_mirror(Klass* k, TRAPS) {
|
|||||||
|
|
||||||
if (k->is_instance_klass()) {
|
if (k->is_instance_klass()) {
|
||||||
InstanceKlass *ik = InstanceKlass::cast(k);
|
InstanceKlass *ik = InstanceKlass::cast(k);
|
||||||
assert(ik->signers() == NULL && !k->has_signer_and_not_archived(),
|
assert(ik->signers() == NULL, "class with signer should have been excluded");
|
||||||
"class with signer cannot be supported");
|
|
||||||
|
|
||||||
if (!(ik->is_shared_boot_class() || ik->is_shared_platform_class() ||
|
if (!(ik->is_shared_boot_class() || ik->is_shared_platform_class() ||
|
||||||
ik->is_shared_app_class())) {
|
ik->is_shared_app_class())) {
|
||||||
|
@ -847,7 +847,7 @@ void StringTable::copy_shared_string_table(CompactHashtableWriter* writer) {
|
|||||||
assert(HeapShared::is_heap_object_archiving_allowed(), "must be");
|
assert(HeapShared::is_heap_object_archiving_allowed(), "must be");
|
||||||
|
|
||||||
CopyToArchive copy(writer);
|
CopyToArchive copy(writer);
|
||||||
StringTable::the_table()->_local_table->do_scan(Thread::current(), copy);
|
StringTable::the_table()->_local_table->do_safepoint_scan(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringTable::write_to_archive() {
|
void StringTable::write_to_archive() {
|
||||||
|
@ -57,9 +57,6 @@
|
|||||||
#define ON_STACK_BUFFER_LENGTH 128
|
#define ON_STACK_BUFFER_LENGTH 128
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
inline Symbol* read_symbol_from_compact_hashtable(address base_address, u4 offset) {
|
|
||||||
return (Symbol*)(base_address + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) {
|
inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) {
|
||||||
if (value->equals(key, len)) {
|
if (value->equals(key, len)) {
|
||||||
@ -70,9 +67,8 @@ inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static CompactHashtable<
|
static OffsetCompactHashtable<
|
||||||
const char*, Symbol*,
|
const char*, Symbol*,
|
||||||
read_symbol_from_compact_hashtable,
|
|
||||||
symbol_equals_compact_hashtable_entry
|
symbol_equals_compact_hashtable_entry
|
||||||
> _shared_table;
|
> _shared_table;
|
||||||
|
|
||||||
@ -281,7 +277,7 @@ public:
|
|||||||
void SymbolTable::metaspace_pointers_do(MetaspaceClosure* it) {
|
void SymbolTable::metaspace_pointers_do(MetaspaceClosure* it) {
|
||||||
assert(DumpSharedSpaces, "called only during dump time");
|
assert(DumpSharedSpaces, "called only during dump time");
|
||||||
MetaspacePointersDo mpd(it);
|
MetaspacePointersDo mpd(it);
|
||||||
SymbolTable::the_table()->_local_table->do_scan(Thread::current(), mpd);
|
SymbolTable::the_table()->_local_table->do_safepoint_scan(mpd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol* SymbolTable::lookup_dynamic(const char* name,
|
Symbol* SymbolTable::lookup_dynamic(const char* name,
|
||||||
@ -637,23 +633,14 @@ struct CopyToArchive : StackObj {
|
|||||||
unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
|
unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
|
||||||
assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
|
assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
|
||||||
"must not rehash during dumping");
|
"must not rehash during dumping");
|
||||||
|
_writer->add(fixed_hash, MetaspaceShared::object_delta_u4(sym));
|
||||||
uintx deltax = MetaspaceShared::object_delta(sym);
|
|
||||||
// When the symbols are stored into the archive, we already check that
|
|
||||||
// they won't be more than MAX_SHARED_DELTA from the base address, or
|
|
||||||
// else the dumping would have been aborted.
|
|
||||||
assert(deltax <= MAX_SHARED_DELTA, "must not be");
|
|
||||||
u4 delta = u4(deltax);
|
|
||||||
|
|
||||||
// add to the compact table
|
|
||||||
_writer->add(fixed_hash, delta);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void SymbolTable::copy_shared_symbol_table(CompactHashtableWriter* writer) {
|
void SymbolTable::copy_shared_symbol_table(CompactHashtableWriter* writer) {
|
||||||
CopyToArchive copy(writer);
|
CopyToArchive copy(writer);
|
||||||
SymbolTable::the_table()->_local_table->do_scan(Thread::current(), copy);
|
SymbolTable::the_table()->_local_table->do_safepoint_scan(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::write_to_archive() {
|
void SymbolTable::write_to_archive() {
|
||||||
|
@ -96,7 +96,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
PlaceholderTable* SystemDictionary::_placeholders = NULL;
|
PlaceholderTable* SystemDictionary::_placeholders = NULL;
|
||||||
Dictionary* SystemDictionary::_shared_dictionary = NULL;
|
|
||||||
LoaderConstraintTable* SystemDictionary::_loader_constraints = NULL;
|
LoaderConstraintTable* SystemDictionary::_loader_constraints = NULL;
|
||||||
ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL;
|
ResolutionErrorTable* SystemDictionary::_resolution_errors = NULL;
|
||||||
SymbolPropertyTable* SystemDictionary::_invoke_method_table = NULL;
|
SymbolPropertyTable* SystemDictionary::_invoke_method_table = NULL;
|
||||||
@ -355,7 +354,7 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* child_name,
|
|||||||
assert(!FieldType::is_array(super_name), "invalid super class name");
|
assert(!FieldType::is_array(super_name), "invalid super class name");
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
if (DumpSharedSpaces) {
|
if (DumpSharedSpaces) {
|
||||||
// Special processing for CDS dump time.
|
// Special processing for handling UNREGISTERED shared classes.
|
||||||
InstanceKlass* k = SystemDictionaryShared::dump_time_resolve_super_or_fail(child_name,
|
InstanceKlass* k = SystemDictionaryShared::dump_time_resolve_super_or_fail(child_name,
|
||||||
super_name, class_loader, protection_domain, is_superclass, CHECK_NULL);
|
super_name, class_loader, protection_domain, is_superclass, CHECK_NULL);
|
||||||
if (k) {
|
if (k) {
|
||||||
@ -1163,39 +1162,11 @@ InstanceKlass* SystemDictionary::resolve_from_stream(Symbol* class_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
#if INCLUDE_CDS
|
||||||
void SystemDictionary::set_shared_dictionary(HashtableBucket<mtClass>* t, int length,
|
// Load a class for boot loader from the shared spaces. This also
|
||||||
int number_of_entries) {
|
// forces the super class and all interfaces to be loaded.
|
||||||
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
|
|
||||||
assert(length == _shared_dictionary_size * sizeof(HashtableBucket<mtClass>),
|
|
||||||
"bad shared dictionary size.");
|
|
||||||
_shared_dictionary = new Dictionary(ClassLoaderData::the_null_class_loader_data(),
|
|
||||||
_shared_dictionary_size, t, number_of_entries,
|
|
||||||
false /* explicitly set _resizable to false */);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// If there is a shared dictionary, then find the entry for the
|
|
||||||
// given shared system class, if any.
|
|
||||||
|
|
||||||
InstanceKlass* SystemDictionary::find_shared_class(Symbol* class_name) {
|
|
||||||
if (shared_dictionary() != NULL) {
|
|
||||||
unsigned int d_hash = shared_dictionary()->compute_hash(class_name);
|
|
||||||
int d_index = shared_dictionary()->hash_to_index(d_hash);
|
|
||||||
|
|
||||||
return shared_dictionary()->find_shared_class(d_index, d_hash, class_name);
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Load a class for boot loader from the shared spaces (found through
|
|
||||||
// the shared system dictionary). Force the super class and all interfaces
|
|
||||||
// to be loaded.
|
|
||||||
InstanceKlass* SystemDictionary::load_shared_boot_class(Symbol* class_name,
|
InstanceKlass* SystemDictionary::load_shared_boot_class(Symbol* class_name,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
InstanceKlass* ik = find_shared_class(class_name);
|
InstanceKlass* ik = SystemDictionaryShared::find_builtin_class(class_name);
|
||||||
// Make sure we only return the boot class.
|
|
||||||
if (ik != NULL && ik->is_shared_boot_class()) {
|
if (ik != NULL && ik->is_shared_boot_class()) {
|
||||||
return load_shared_class(ik, Handle(), Handle(), THREAD);
|
return load_shared_class(ik, Handle(), Handle(), THREAD);
|
||||||
}
|
}
|
||||||
@ -1410,18 +1381,6 @@ InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik,
|
|||||||
}
|
}
|
||||||
return ik;
|
return ik;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemDictionary::clear_invoke_method_table() {
|
|
||||||
SymbolPropertyEntry* spe = NULL;
|
|
||||||
for (int index = 0; index < _invoke_method_table->table_size(); index++) {
|
|
||||||
SymbolPropertyEntry* p = _invoke_method_table->bucket(index);
|
|
||||||
while (p != NULL) {
|
|
||||||
spe = p;
|
|
||||||
p = p->next();
|
|
||||||
_invoke_method_table->free_entry(spe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // INCLUDE_CDS
|
#endif // INCLUDE_CDS
|
||||||
|
|
||||||
InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
|
InstanceKlass* SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
|
||||||
@ -1845,15 +1804,14 @@ void SystemDictionary::add_to_hierarchy(InstanceKlass* k, TRAPS) {
|
|||||||
|
|
||||||
// Assumes classes in the SystemDictionary are only unloaded at a safepoint
|
// Assumes classes in the SystemDictionary are only unloaded at a safepoint
|
||||||
// Note: anonymous classes are not in the SD.
|
// Note: anonymous classes are not in the SD.
|
||||||
bool SystemDictionary::do_unloading(GCTimer* gc_timer,
|
bool SystemDictionary::do_unloading(GCTimer* gc_timer) {
|
||||||
bool do_cleaning) {
|
|
||||||
|
|
||||||
bool unloading_occurred;
|
bool unloading_occurred;
|
||||||
{
|
{
|
||||||
GCTraceTime(Debug, gc, phases) t("ClassLoaderData", gc_timer);
|
GCTraceTime(Debug, gc, phases) t("ClassLoaderData", gc_timer);
|
||||||
|
|
||||||
// First, mark for unload all ClassLoaderData referencing a dead class loader.
|
// First, mark for unload all ClassLoaderData referencing a dead class loader.
|
||||||
unloading_occurred = ClassLoaderDataGraph::do_unloading(do_cleaning);
|
unloading_occurred = ClassLoaderDataGraph::do_unloading();
|
||||||
if (unloading_occurred) {
|
if (unloading_occurred) {
|
||||||
JFR_ONLY(Jfr::on_unloading_classes();)
|
JFR_ONLY(Jfr::on_unloading_classes();)
|
||||||
ClassLoaderDataGraph::clean_module_and_package_info();
|
ClassLoaderDataGraph::clean_module_and_package_info();
|
||||||
@ -1883,7 +1841,7 @@ bool SystemDictionary::do_unloading(GCTimer* gc_timer,
|
|||||||
_pd_cache_table->trigger_cleanup();
|
_pd_cache_table->trigger_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_cleaning) {
|
{
|
||||||
GCTraceTime(Debug, gc, phases) t("ResolvedMethodTable", gc_timer);
|
GCTraceTime(Debug, gc, phases) t("ResolvedMethodTable", gc_timer);
|
||||||
ResolvedMethodTable::trigger_cleanup();
|
ResolvedMethodTable::trigger_cleanup();
|
||||||
}
|
}
|
||||||
@ -1901,11 +1859,6 @@ void SystemDictionary::oops_do(OopClosure* f) {
|
|||||||
invoke_method_table()->oops_do(f);
|
invoke_method_table()->oops_do(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CDS: scan and relocate all classes in the system dictionary.
|
|
||||||
void SystemDictionary::classes_do(MetaspaceClosure* it) {
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CDS: scan and relocate all classes referenced by _well_known_klasses[].
|
// CDS: scan and relocate all classes referenced by _well_known_klasses[].
|
||||||
void SystemDictionary::well_known_klasses_do(MetaspaceClosure* it) {
|
void SystemDictionary::well_known_klasses_do(MetaspaceClosure* it) {
|
||||||
for (int id = FIRST_WKID; id < WKID_LIMIT; id++) {
|
for (int id = FIRST_WKID; id < WKID_LIMIT; id++) {
|
||||||
@ -1921,22 +1874,6 @@ void SystemDictionary::methods_do(void f(Method*)) {
|
|||||||
invoke_method_table()->methods_do(f);
|
invoke_method_table()->methods_do(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
class RemoveClassesClosure : public CLDClosure {
|
|
||||||
public:
|
|
||||||
void do_cld(ClassLoaderData* cld) {
|
|
||||||
if (cld->is_system_class_loader_data() || cld->is_platform_class_loader_data()) {
|
|
||||||
cld->dictionary()->remove_classes_in_error_state();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void SystemDictionary::remove_classes_in_error_state() {
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->remove_classes_in_error_state();
|
|
||||||
RemoveClassesClosure rcc;
|
|
||||||
MutexLocker ml(ClassLoaderDataGraph_lock);
|
|
||||||
ClassLoaderDataGraph::cld_do(&rcc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
||||||
@ -2039,6 +1976,7 @@ void SystemDictionary::resolve_well_known_classes(TRAPS) {
|
|||||||
HeapShared::fixup_mapped_heap_regions();
|
HeapShared::fixup_mapped_heap_regions();
|
||||||
|
|
||||||
// Initialize the constant pool for the Object_class
|
// Initialize the constant pool for the Object_class
|
||||||
|
assert(Object_klass()->is_shared(), "must be");
|
||||||
Object_klass()->constants()->restore_unshareable_info(CHECK);
|
Object_klass()->constants()->restore_unshareable_info(CHECK);
|
||||||
resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Class_klass), scan, CHECK);
|
resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Class_klass), scan, CHECK);
|
||||||
} else
|
} else
|
||||||
@ -2922,40 +2860,10 @@ ProtectionDomainCacheEntry* SystemDictionary::cache_get(Handle protection_domain
|
|||||||
return _pd_cache_table->get(protection_domain);
|
return _pd_cache_table->get(protection_domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INCLUDE_CDS
|
|
||||||
void SystemDictionary::reorder_dictionary_for_sharing() {
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->reorder_dictionary_for_sharing();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t SystemDictionary::count_bytes_for_buckets() {
|
|
||||||
return ClassLoaderData::the_null_class_loader_data()->dictionary()->count_bytes_for_buckets();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SystemDictionary::count_bytes_for_table() {
|
|
||||||
return ClassLoaderData::the_null_class_loader_data()->dictionary()->count_bytes_for_table();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionary::copy_buckets(char* top, char* end) {
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->copy_buckets(top, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionary::copy_table(char* top, char* end) {
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->copy_table(top, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
void SystemDictionary::print_shared(outputStream *st) {
|
|
||||||
shared_dictionary()->print_on(st);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionary::print_on(outputStream *st) {
|
void SystemDictionary::print_on(outputStream *st) {
|
||||||
if (shared_dictionary() != NULL) {
|
CDS_ONLY(SystemDictionaryShared::print_on(st));
|
||||||
st->print_cr("Shared Dictionary");
|
|
||||||
shared_dictionary()->print_on(st);
|
|
||||||
st->cr();
|
|
||||||
}
|
|
||||||
|
|
||||||
GCMutexLocker mu(SystemDictionary_lock);
|
GCMutexLocker mu(SystemDictionary_lock);
|
||||||
|
|
||||||
ClassLoaderDataGraph::print_dictionary(st);
|
ClassLoaderDataGraph::print_dictionary(st);
|
||||||
@ -2997,9 +2905,7 @@ void SystemDictionary::dump(outputStream *st, bool verbose) {
|
|||||||
if (verbose) {
|
if (verbose) {
|
||||||
print_on(st);
|
print_on(st);
|
||||||
} else {
|
} else {
|
||||||
if (shared_dictionary() != NULL) {
|
CDS_ONLY(SystemDictionaryShared::print_table_statistics(st));
|
||||||
shared_dictionary()->print_table_statistics(st, "Shared Dictionary");
|
|
||||||
}
|
|
||||||
ClassLoaderDataGraph::print_dictionary_statistics(st);
|
ClassLoaderDataGraph::print_dictionary_statistics(st);
|
||||||
placeholders()->print_table_statistics(st, "Placeholder Table");
|
placeholders()->print_table_statistics(st, "Placeholder Table");
|
||||||
constraints()->print_table_statistics(st, "LoaderConstraints Table");
|
constraints()->print_table_statistics(st, "LoaderConstraints Table");
|
||||||
@ -3032,60 +2938,6 @@ int SystemDictionaryDCmd::num_arguments() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CombineDictionariesClosure : public CLDClosure {
|
|
||||||
private:
|
|
||||||
Dictionary* _master_dictionary;
|
|
||||||
public:
|
|
||||||
CombineDictionariesClosure(Dictionary* master_dictionary) :
|
|
||||||
_master_dictionary(master_dictionary) {}
|
|
||||||
void do_cld(ClassLoaderData* cld) {
|
|
||||||
ResourceMark rm;
|
|
||||||
if (cld->is_unsafe_anonymous()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (cld->is_system_class_loader_data() || cld->is_platform_class_loader_data()) {
|
|
||||||
for (int i = 0; i < cld->dictionary()->table_size(); ++i) {
|
|
||||||
Dictionary* curr_dictionary = cld->dictionary();
|
|
||||||
DictionaryEntry* p = curr_dictionary->bucket(i);
|
|
||||||
while (p != NULL) {
|
|
||||||
Symbol* name = p->instance_klass()->name();
|
|
||||||
unsigned int d_hash = _master_dictionary->compute_hash(name);
|
|
||||||
int d_index = _master_dictionary->hash_to_index(d_hash);
|
|
||||||
DictionaryEntry* next = p->next();
|
|
||||||
if (p->literal()->class_loader_data() != cld) {
|
|
||||||
// This is an initiating class loader entry; don't use it
|
|
||||||
log_trace(cds)("Skipping initiating cl entry: %s", name->as_C_string());
|
|
||||||
curr_dictionary->free_entry(p);
|
|
||||||
} else {
|
|
||||||
log_trace(cds)("Moved to boot dictionary: %s", name->as_C_string());
|
|
||||||
curr_dictionary->unlink_entry(p);
|
|
||||||
p->set_pd_set(NULL); // pd_set is runtime only information and will be reconstructed.
|
|
||||||
_master_dictionary->add_entry(d_index, p);
|
|
||||||
}
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
*curr_dictionary->bucket_addr(i) = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Combining platform and system loader dictionaries into boot loader dictionary.
|
|
||||||
// During run time, we only have one shared dictionary.
|
|
||||||
void SystemDictionary::combine_shared_dictionaries() {
|
|
||||||
assert(DumpSharedSpaces, "dump time only");
|
|
||||||
Dictionary* master_dictionary = ClassLoaderData::the_null_class_loader_data()->dictionary();
|
|
||||||
CombineDictionariesClosure cdc(master_dictionary);
|
|
||||||
ClassLoaderDataGraph::cld_do(&cdc);
|
|
||||||
|
|
||||||
// These tables are no longer valid or necessary. Keeping them around will
|
|
||||||
// cause SystemDictionary::verify() to fail. Let's empty them.
|
|
||||||
_placeholders = new PlaceholderTable(_placeholder_table_size);
|
|
||||||
_loader_constraints = new LoaderConstraintTable(_loader_constraint_size);
|
|
||||||
|
|
||||||
NOT_PRODUCT(SystemDictionary::verify());
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionary::initialize_oop_storage() {
|
void SystemDictionary::initialize_oop_storage() {
|
||||||
_vm_weak_oop_storage =
|
_vm_weak_oop_storage =
|
||||||
new OopStorage("VM Weak Oop Handles",
|
new OopStorage("VM Weak Oop Handles",
|
||||||
|
@ -347,13 +347,7 @@ public:
|
|||||||
|
|
||||||
// Unload (that is, break root links to) all unmarked classes and
|
// Unload (that is, break root links to) all unmarked classes and
|
||||||
// loaders. Returns "true" iff something was unloaded.
|
// loaders. Returns "true" iff something was unloaded.
|
||||||
static bool do_unloading(GCTimer* gc_timer,
|
static bool do_unloading(GCTimer* gc_timer);
|
||||||
bool do_cleaning = true);
|
|
||||||
|
|
||||||
// Used by DumpSharedSpaces only to remove classes that failed verification
|
|
||||||
static void remove_classes_in_error_state();
|
|
||||||
|
|
||||||
static int calculate_systemdictionary_size(int loadedclasses);
|
|
||||||
|
|
||||||
// Applies "f->do_oop" to all root oops in the system dictionary.
|
// Applies "f->do_oop" to all root oops in the system dictionary.
|
||||||
static void oops_do(OopClosure* f);
|
static void oops_do(OopClosure* f);
|
||||||
@ -365,19 +359,9 @@ public:
|
|||||||
static ProtectionDomainCacheTable* pd_cache_table() { return _pd_cache_table; }
|
static ProtectionDomainCacheTable* pd_cache_table() { return _pd_cache_table; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Sharing support.
|
|
||||||
static void reorder_dictionary_for_sharing() NOT_CDS_RETURN;
|
|
||||||
static void combine_shared_dictionaries();
|
|
||||||
static size_t count_bytes_for_buckets();
|
|
||||||
static size_t count_bytes_for_table();
|
|
||||||
static void copy_buckets(char* top, char* end);
|
|
||||||
static void copy_table(char* top, char* end);
|
|
||||||
static void set_shared_dictionary(HashtableBucket<mtClass>* t, int length,
|
|
||||||
int number_of_entries);
|
|
||||||
// Printing
|
// Printing
|
||||||
static void print() { return print_on(tty); }
|
static void print() { return print_on(tty); }
|
||||||
static void print_on(outputStream* st);
|
static void print_on(outputStream* st);
|
||||||
static void print_shared(outputStream* st);
|
|
||||||
static void dump(outputStream* st, bool verbose);
|
static void dump(outputStream* st, bool verbose);
|
||||||
|
|
||||||
// Monotonically increasing counter which grows as classes are
|
// Monotonically increasing counter which grows as classes are
|
||||||
@ -580,7 +564,6 @@ public:
|
|||||||
_loader_constraint_size = 107, // number of entries in constraint table
|
_loader_constraint_size = 107, // number of entries in constraint table
|
||||||
_resolution_error_size = 107, // number of entries in resolution error table
|
_resolution_error_size = 107, // number of entries in resolution error table
|
||||||
_invoke_method_size = 139, // number of entries in invoke method table
|
_invoke_method_size = 139, // number of entries in invoke method table
|
||||||
_shared_dictionary_size = 1009, // number of entries in shared dictionary
|
|
||||||
_placeholder_table_size = 1009 // number of entries in hash table for placeholders
|
_placeholder_table_size = 1009 // number of entries in hash table for placeholders
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -590,9 +573,6 @@ public:
|
|||||||
// Hashtable holding placeholders for classes being loaded.
|
// Hashtable holding placeholders for classes being loaded.
|
||||||
static PlaceholderTable* _placeholders;
|
static PlaceholderTable* _placeholders;
|
||||||
|
|
||||||
// Hashtable holding classes from the shared archive.
|
|
||||||
static Dictionary* _shared_dictionary;
|
|
||||||
|
|
||||||
// Monotonically increasing counter which grows with
|
// Monotonically increasing counter which grows with
|
||||||
// loading classes as well as hot-swapping and breakpoint setting
|
// loading classes as well as hot-swapping and breakpoint setting
|
||||||
// and removal.
|
// and removal.
|
||||||
@ -623,7 +603,6 @@ protected:
|
|||||||
|
|
||||||
friend class VM_PopulateDumpSharedSpace;
|
friend class VM_PopulateDumpSharedSpace;
|
||||||
friend class TraversePlaceholdersClosure;
|
friend class TraversePlaceholdersClosure;
|
||||||
static Dictionary* shared_dictionary() { return _shared_dictionary; }
|
|
||||||
static PlaceholderTable* placeholders() { return _placeholders; }
|
static PlaceholderTable* placeholders() { return _placeholders; }
|
||||||
static LoaderConstraintTable* constraints() { return _loader_constraints; }
|
static LoaderConstraintTable* constraints() { return _loader_constraints; }
|
||||||
static ResolutionErrorTable* resolution_errors() { return _resolution_errors; }
|
static ResolutionErrorTable* resolution_errors() { return _resolution_errors; }
|
||||||
@ -663,7 +642,6 @@ protected:
|
|||||||
public:
|
public:
|
||||||
static bool is_system_class_loader(oop class_loader);
|
static bool is_system_class_loader(oop class_loader);
|
||||||
static bool is_platform_class_loader(oop class_loader);
|
static bool is_platform_class_loader(oop class_loader);
|
||||||
static void clear_invoke_method_table();
|
|
||||||
|
|
||||||
// Returns TRUE if the method is a non-public member of class java.lang.Object.
|
// Returns TRUE if the method is a non-public member of class java.lang.Object.
|
||||||
static bool is_nonpublic_Object_method(Method* m) {
|
static bool is_nonpublic_Object_method(Method* m) {
|
||||||
@ -675,8 +653,6 @@ public:
|
|||||||
static OopStorage* vm_weak_oop_storage();
|
static OopStorage* vm_weak_oop_storage();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static InstanceKlass* find_shared_class(Symbol* class_name);
|
|
||||||
|
|
||||||
// Setup link to hierarchy
|
// Setup link to hierarchy
|
||||||
static void add_to_hierarchy(InstanceKlass* k, TRAPS);
|
static void add_to_hierarchy(InstanceKlass* k, TRAPS);
|
||||||
|
|
||||||
|
@ -53,12 +53,277 @@
|
|||||||
#include "runtime/javaCalls.hpp"
|
#include "runtime/javaCalls.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "utilities/hashtable.inline.hpp"
|
#include "utilities/hashtable.inline.hpp"
|
||||||
|
#include "utilities/resourceHash.hpp"
|
||||||
#include "utilities/stringUtils.hpp"
|
#include "utilities/stringUtils.hpp"
|
||||||
|
|
||||||
|
|
||||||
objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL;
|
objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL;
|
||||||
objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL;
|
objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL;
|
||||||
objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL;
|
objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL;
|
||||||
|
DEBUG_ONLY(bool SystemDictionaryShared::_checked_excluded_classes = false;)
|
||||||
|
|
||||||
|
class DumpTimeSharedClassInfo: public CHeapObj<mtClass> {
|
||||||
|
public:
|
||||||
|
struct DTConstraint {
|
||||||
|
Symbol* _name;
|
||||||
|
Symbol* _from_name;
|
||||||
|
DTConstraint() : _name(NULL), _from_name(NULL) {}
|
||||||
|
DTConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
InstanceKlass* _klass;
|
||||||
|
int _id;
|
||||||
|
int _clsfile_size;
|
||||||
|
int _clsfile_crc32;
|
||||||
|
bool _excluded;
|
||||||
|
GrowableArray<DTConstraint>* _verifier_constraints;
|
||||||
|
GrowableArray<char>* _verifier_constraint_flags;
|
||||||
|
|
||||||
|
DumpTimeSharedClassInfo() {
|
||||||
|
_klass = NULL;
|
||||||
|
_id = -1;
|
||||||
|
_clsfile_size = -1;
|
||||||
|
_clsfile_crc32 = -1;
|
||||||
|
_excluded = false;
|
||||||
|
_verifier_constraints = NULL;
|
||||||
|
_verifier_constraint_flags = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||||
|
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
|
||||||
|
|
||||||
|
bool is_builtin() {
|
||||||
|
return SystemDictionaryShared::is_builtin(_klass);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_constraints() {
|
||||||
|
if (_verifier_constraint_flags != NULL) {
|
||||||
|
return _verifier_constraint_flags->length();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void metaspace_pointers_do(MetaspaceClosure* it) {
|
||||||
|
it->push(&_klass);
|
||||||
|
if (_verifier_constraints != NULL) {
|
||||||
|
for (int i = 0; i < _verifier_constraints->length(); i++) {
|
||||||
|
DTConstraint* cons = _verifier_constraints->adr_at(i);
|
||||||
|
it->push(&cons->_name);
|
||||||
|
it->push(&cons->_from_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DumpTimeSharedClassTable: public ResourceHashtable<
|
||||||
|
InstanceKlass*,
|
||||||
|
DumpTimeSharedClassInfo,
|
||||||
|
primitive_hash<InstanceKlass*>,
|
||||||
|
primitive_equals<InstanceKlass*>,
|
||||||
|
15889, // prime number
|
||||||
|
ResourceObj::C_HEAP>
|
||||||
|
{
|
||||||
|
int _builtin_count;
|
||||||
|
int _unregistered_count;
|
||||||
|
public:
|
||||||
|
DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k) {
|
||||||
|
DumpTimeSharedClassInfo* p = get(k);
|
||||||
|
if (p == NULL) {
|
||||||
|
assert(!SystemDictionaryShared::checked_excluded_classes(),
|
||||||
|
"no new classes can be added after check_excluded_classes");
|
||||||
|
put(k, DumpTimeSharedClassInfo());
|
||||||
|
p = get(k);
|
||||||
|
assert(p != NULL, "sanity");
|
||||||
|
p->_klass = k;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CountClassByCategory : StackObj {
|
||||||
|
DumpTimeSharedClassTable* _table;
|
||||||
|
public:
|
||||||
|
CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {}
|
||||||
|
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||||||
|
if (SystemDictionaryShared::is_builtin(k)) {
|
||||||
|
++ _table->_builtin_count;
|
||||||
|
} else {
|
||||||
|
++ _table->_unregistered_count;
|
||||||
|
}
|
||||||
|
return true; // keep on iterating
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void update_counts() {
|
||||||
|
CountClassByCategory counter(this);
|
||||||
|
iterate(&counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
int count_of(bool is_builtin) const {
|
||||||
|
if (is_builtin) {
|
||||||
|
return _builtin_count;
|
||||||
|
} else {
|
||||||
|
return _unregistered_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RunTimeSharedClassInfo {
|
||||||
|
public:
|
||||||
|
struct CrcInfo {
|
||||||
|
int _clsfile_size;
|
||||||
|
int _clsfile_crc32;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is different than DumpTimeSharedClassInfo::DTConstraint. We use
|
||||||
|
// u4 instead of Symbol* to save space on 64-bit CPU.
|
||||||
|
struct RTConstraint {
|
||||||
|
u4 _name;
|
||||||
|
u4 _from_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
InstanceKlass* _klass;
|
||||||
|
int _num_constraints;
|
||||||
|
|
||||||
|
// optional CrcInfo _crc; (only for UNREGISTERED classes)
|
||||||
|
// optional RTConstraint _verifier_constraints[_num_constraints]
|
||||||
|
// optional char _verifier_constraint_flags[_num_constraints]
|
||||||
|
|
||||||
|
private:
|
||||||
|
static size_t header_size_size() {
|
||||||
|
return sizeof(RunTimeSharedClassInfo);
|
||||||
|
}
|
||||||
|
static size_t crc_size(InstanceKlass* klass) {
|
||||||
|
if (!SystemDictionaryShared::is_builtin(klass)) {
|
||||||
|
return sizeof(CrcInfo);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static size_t verifier_constraints_size(int num_constraints) {
|
||||||
|
return sizeof(RTConstraint) * num_constraints;
|
||||||
|
}
|
||||||
|
static size_t verifier_constraint_flags_size(int num_constraints) {
|
||||||
|
return sizeof(char) * num_constraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static size_t byte_size(InstanceKlass* klass, int num_constraints) {
|
||||||
|
return header_size_size() +
|
||||||
|
crc_size(klass) +
|
||||||
|
verifier_constraints_size(num_constraints) +
|
||||||
|
verifier_constraint_flags_size(num_constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t crc_offset() const {
|
||||||
|
return header_size_size();
|
||||||
|
}
|
||||||
|
size_t verifier_constraints_offset() const {
|
||||||
|
return crc_offset() + crc_size(_klass);
|
||||||
|
}
|
||||||
|
size_t verifier_constraint_flags_offset() const {
|
||||||
|
return verifier_constraints_offset() + verifier_constraints_size(_num_constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_constraint_offset(int i) const {
|
||||||
|
assert(0 <= i && i < _num_constraints, "sanity");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CrcInfo* crc() const {
|
||||||
|
assert(crc_size(_klass) > 0, "must be");
|
||||||
|
return (CrcInfo*)(address(this) + crc_offset());
|
||||||
|
}
|
||||||
|
RTConstraint* verifier_constraints() {
|
||||||
|
assert(_num_constraints > 0, "sanity");
|
||||||
|
return (RTConstraint*)(address(this) + verifier_constraints_offset());
|
||||||
|
}
|
||||||
|
RTConstraint* verifier_constraint_at(int i) {
|
||||||
|
check_constraint_offset(i);
|
||||||
|
return verifier_constraints() + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* verifier_constraint_flags() {
|
||||||
|
assert(_num_constraints > 0, "sanity");
|
||||||
|
return (char*)(address(this) + verifier_constraint_flags_offset());
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(DumpTimeSharedClassInfo& info) {
|
||||||
|
_klass = info._klass;
|
||||||
|
_num_constraints = info.num_constraints();
|
||||||
|
if (!SystemDictionaryShared::is_builtin(_klass)) {
|
||||||
|
CrcInfo* c = crc();
|
||||||
|
c->_clsfile_size = info._clsfile_size;
|
||||||
|
c->_clsfile_crc32 = info._clsfile_crc32;
|
||||||
|
}
|
||||||
|
if (_num_constraints > 0) {
|
||||||
|
RTConstraint* constraints = verifier_constraints();
|
||||||
|
char* flags = verifier_constraint_flags();
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < _num_constraints; i++) {
|
||||||
|
constraints[i]._name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._name);
|
||||||
|
constraints[i]._from_name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._from_name);
|
||||||
|
}
|
||||||
|
for (i = 0; i < _num_constraints; i++) {
|
||||||
|
flags[i] = info._verifier_constraint_flags->at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(int clsfile_size, int clsfile_crc32) const {
|
||||||
|
return crc()->_clsfile_size == clsfile_size &&
|
||||||
|
crc()->_clsfile_crc32 == clsfile_crc32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol* get_constraint_name(int i) {
|
||||||
|
return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_name);
|
||||||
|
}
|
||||||
|
Symbol* get_constraint_from_name(int i) {
|
||||||
|
return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_from_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
char get_constraint_flag(int i) {
|
||||||
|
check_constraint_offset(i);
|
||||||
|
return verifier_constraint_flags()[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ArchiveCompactor::allocate() has reserved a pointer immediately before
|
||||||
|
// archived InstanceKlasses. We can use this slot to do a quick
|
||||||
|
// lookup of InstanceKlass* -> RunTimeSharedClassInfo* without
|
||||||
|
// building a new hashtable.
|
||||||
|
//
|
||||||
|
// info_pointer_addr(klass) --> 0x0100 RunTimeSharedClassInfo*
|
||||||
|
// InstanceKlass* klass --> 0x0108 <C++ vtbl>
|
||||||
|
// 0x0110 fields from Klass ...
|
||||||
|
static RunTimeSharedClassInfo** info_pointer_addr(InstanceKlass* klass) {
|
||||||
|
return &((RunTimeSharedClassInfo**)klass)[-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) {
|
||||||
|
return *info_pointer_addr(klass);
|
||||||
|
}
|
||||||
|
static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) {
|
||||||
|
*info_pointer_addr(klass) = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS
|
||||||
|
static inline bool EQUALS(
|
||||||
|
const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) {
|
||||||
|
return (value->_klass->name() == key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RunTimeSharedDictionary : public OffsetCompactHashtable<
|
||||||
|
Symbol*,
|
||||||
|
const RunTimeSharedClassInfo*,
|
||||||
|
RunTimeSharedClassInfo::EQUALS> {};
|
||||||
|
|
||||||
|
static DumpTimeSharedClassTable* _dumptime_table = NULL;
|
||||||
|
static RunTimeSharedDictionary _builtin_dictionary;
|
||||||
|
static RunTimeSharedDictionary _unregistered_dictionary;
|
||||||
|
|
||||||
oop SystemDictionaryShared::shared_protection_domain(int index) {
|
oop SystemDictionaryShared::shared_protection_domain(int index) {
|
||||||
return _shared_protection_domains->obj_at(index);
|
return _shared_protection_domains->obj_at(index);
|
||||||
@ -478,9 +743,8 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shared_dictionary() != NULL &&
|
if (SystemDictionary::is_system_class_loader(class_loader()) ||
|
||||||
(SystemDictionary::is_system_class_loader(class_loader()) ||
|
SystemDictionary::is_platform_class_loader(class_loader())) {
|
||||||
SystemDictionary::is_platform_class_loader(class_loader()))) {
|
|
||||||
// Fix for 4474172; see evaluation for more details
|
// Fix for 4474172; see evaluation for more details
|
||||||
class_loader = Handle(
|
class_loader = Handle(
|
||||||
THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
|
THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
|
||||||
@ -523,8 +787,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
|
|||||||
InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
|
InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
|
||||||
Symbol* class_name, Handle class_loader, TRAPS) {
|
Symbol* class_name, Handle class_loader, TRAPS) {
|
||||||
assert(UseSharedSpaces, "must be");
|
assert(UseSharedSpaces, "must be");
|
||||||
assert(shared_dictionary() != NULL, "already checked");
|
InstanceKlass* ik = find_builtin_class(class_name);
|
||||||
InstanceKlass* ik = shared_dictionary()->find_class_for_builtin_loader(class_name);
|
|
||||||
|
|
||||||
if (ik != NULL) {
|
if (ik != NULL) {
|
||||||
if ((ik->is_shared_app_class() &&
|
if ((ik->is_shared_app_class() &&
|
||||||
@ -536,7 +799,6 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
|
|||||||
return load_shared_class(ik, class_loader, protection_domain, THREAD);
|
return load_shared_class(ik, class_loader, protection_domain, THREAD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,12 +836,12 @@ void SystemDictionaryShared::allocate_shared_data_arrays(int size, TRAPS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This function is called for loading only UNREGISTERED classes
|
// This function is called for loading only UNREGISTERED classes
|
||||||
InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_name,
|
InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name,
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const ClassFileStream* cfs,
|
const ClassFileStream* cfs,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
if (shared_dictionary() == NULL) {
|
if (!UseSharedSpaces) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (class_name == NULL) { // don't do this for anonymous classes
|
if (class_name == NULL) { // don't do this for anonymous classes
|
||||||
@ -592,27 +854,18 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_na
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
|
const RunTimeSharedClassInfo* record = find_record(&_unregistered_dictionary, class_name);
|
||||||
InstanceKlass* k;
|
if (record == NULL) {
|
||||||
|
|
||||||
{ // UNREGISTERED loader
|
|
||||||
if (!shared_dictionary()->class_exists_for_unregistered_loader(class_name)) {
|
|
||||||
// No classes of this name for unregistered loaders.
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int clsfile_size = cfs->length();
|
int clsfile_size = cfs->length();
|
||||||
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
||||||
|
if (!record->matches(clsfile_size, clsfile_crc32)) {
|
||||||
k = shared_dictionary()->find_class_for_unregistered_loader(class_name,
|
|
||||||
clsfile_size, clsfile_crc32);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k == NULL) { // not archived
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return acquire_class_for_current_thread(k, class_loader,
|
return acquire_class_for_current_thread(record->_klass, class_loader,
|
||||||
protection_domain, THREAD);
|
protection_domain, THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,19 +902,27 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
|
|||||||
return shared_klass;
|
return shared_klass;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemDictionaryShared::add_non_builtin_klass(Symbol* name,
|
static ResourceHashtable<
|
||||||
ClassLoaderData* loader_data,
|
Symbol*, bool,
|
||||||
InstanceKlass* k,
|
primitive_hash<Symbol*>,
|
||||||
TRAPS) {
|
primitive_equals<Symbol*>,
|
||||||
assert(DumpSharedSpaces, "only when dumping");
|
6661, // prime number
|
||||||
assert(boot_loader_dictionary() != NULL, "must be");
|
ResourceObj::C_HEAP> _loaded_unregistered_classes;
|
||||||
|
|
||||||
if (boot_loader_dictionary()->add_non_builtin_klass(name, loader_data, k)) {
|
bool SystemDictionaryShared::add_unregistered_class(InstanceKlass* k, TRAPS) {
|
||||||
MutexLocker mu_r(Compile_lock, THREAD); // not really necessary, but add_to_hierarchy asserts this.
|
assert(DumpSharedSpaces, "only when dumping");
|
||||||
add_to_hierarchy(k, CHECK_0);
|
|
||||||
|
Symbol* name = k->name();
|
||||||
|
if (_loaded_unregistered_classes.get(name) != NULL) {
|
||||||
|
// We don't allow duplicated unregistered classes of the same name.
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
bool isnew = _loaded_unregistered_classes.put(name, true);
|
||||||
|
assert(isnew, "sanity");
|
||||||
|
MutexLocker mu_r(Compile_lock, THREAD); // add_to_hierarchy asserts this.
|
||||||
|
SystemDictionary::add_to_hierarchy(k, CHECK_0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called to resolve the super/interfaces of shared classes for
|
// This function is called to resolve the super/interfaces of shared classes for
|
||||||
@ -698,81 +959,138 @@ InstanceKlass* SystemDictionaryShared::dump_time_resolve_super_or_fail(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SharedMiscInfo {
|
DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) {
|
||||||
InstanceKlass* _klass;
|
if (_dumptime_table == NULL) {
|
||||||
int _clsfile_size;
|
_dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable();
|
||||||
int _clsfile_crc32;
|
}
|
||||||
};
|
return _dumptime_table->find_or_allocate_info_for(k);
|
||||||
|
}
|
||||||
static GrowableArray<SharedMiscInfo>* misc_info_array = NULL;
|
|
||||||
|
|
||||||
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
|
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
|
||||||
assert(DumpSharedSpaces, "only when dumping");
|
assert(DumpSharedSpaces, "only when dumping");
|
||||||
int clsfile_size = cfs->length();
|
assert(!is_builtin(k), "must be unregistered class");
|
||||||
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
|
||||||
|
info->_clsfile_size = cfs->length();
|
||||||
if (misc_info_array == NULL) {
|
info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
||||||
misc_info_array = new (ResourceObj::C_HEAP, mtClass) GrowableArray<SharedMiscInfo>(20, /*c heap*/ true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedMiscInfo misc_info;
|
void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) {
|
||||||
DEBUG_ONLY({
|
(void)find_or_allocate_info_for(k);
|
||||||
for (int i=0; i<misc_info_array->length(); i++) {
|
|
||||||
misc_info = misc_info_array->at(i);
|
|
||||||
assert(misc_info._klass != k, "cannot call set_shared_class_misc_info twice for the same class");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
misc_info._klass = k;
|
|
||||||
misc_info._clsfile_size = clsfile_size;
|
|
||||||
misc_info._clsfile_crc32 = clsfile_crc32;
|
|
||||||
|
|
||||||
misc_info_array->append(misc_info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemDictionaryShared::init_shared_dictionary_entry(InstanceKlass* k, DictionaryEntry* ent) {
|
void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
|
||||||
SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent;
|
_dumptime_table->remove(k);
|
||||||
entry->_id = -1;
|
}
|
||||||
entry->_clsfile_size = -1;
|
|
||||||
entry->_clsfile_crc32 = -1;
|
|
||||||
entry->_verifier_constraints = NULL;
|
|
||||||
entry->_verifier_constraint_flags = NULL;
|
|
||||||
|
|
||||||
if (misc_info_array != NULL) {
|
bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
|
||||||
for (int i=0; i<misc_info_array->length(); i++) {
|
while (k) {
|
||||||
SharedMiscInfo misc_info = misc_info_array->at(i);
|
if (k->name()->equals("jdk/internal/event/Event")) {
|
||||||
if (misc_info._klass == k) {
|
return true;
|
||||||
entry->_clsfile_size = misc_info._clsfile_size;
|
}
|
||||||
entry->_clsfile_crc32 = misc_info._clsfile_crc32;
|
k = k->java_super();
|
||||||
misc_info_array->remove_at(i);
|
}
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
|
||||||
|
ResourceMark rm;
|
||||||
|
log_warning(cds)("Skipping %s: %s", k->name()->as_C_string(), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
|
||||||
|
if (k->class_loader_data()->is_unsafe_anonymous()) {
|
||||||
|
return true; // unsafe anonymous classes are not archived, skip
|
||||||
|
}
|
||||||
|
if (k->is_in_error_state()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (k->shared_classpath_index() < 0 && is_builtin(k)) {
|
||||||
|
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
|
||||||
|
// agent during dump time).
|
||||||
|
warn_excluded(k, "Unsupported location");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (k->signers() != NULL) {
|
||||||
|
// We cannot include signed classes in the archive because the certificates
|
||||||
|
// used during dump time may be different than those used during
|
||||||
|
// runtime (due to expiration, etc).
|
||||||
|
warn_excluded(k, "Signed JAR");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_jfr_event_class(k)) {
|
||||||
|
// We cannot include JFR event classes because they need runtime-specific
|
||||||
|
// instrumentation in order to work with -XX:FlightRecorderOptions=retransform=false.
|
||||||
|
// There are only a small number of these classes, so it's not worthwhile to
|
||||||
|
// support them and make CDS more complicated.
|
||||||
|
warn_excluded(k, "JFR event class");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// k is a class before relocating by ArchiveCompactor
|
||||||
|
void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) {
|
||||||
|
ResourceMark rm;
|
||||||
|
const char* name = k->name()->as_C_string();
|
||||||
|
DumpTimeSharedClassInfo* info = _dumptime_table->get(k);
|
||||||
|
guarantee(info != NULL, "Class %s must be entered into _dumptime_table", name);
|
||||||
|
guarantee(!info->_excluded, "Should not attempt to archive excluded class %s", name);
|
||||||
|
if (is_builtin(k)) {
|
||||||
|
guarantee(k->loader_type() != 0,
|
||||||
|
"Class loader type must be set for BUILTIN class %s", name);
|
||||||
|
} else {
|
||||||
|
guarantee(k->loader_type() == 0,
|
||||||
|
"Class loader type must not be set for UNREGISTERED class %s", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ExcludeDumpTimeSharedClasses : StackObj {
|
||||||
|
public:
|
||||||
|
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||||||
|
if (SystemDictionaryShared::should_be_excluded(k)) {
|
||||||
|
info._excluded = true;
|
||||||
}
|
}
|
||||||
|
return true; // keep on iterating
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemDictionaryShared::check_excluded_classes() {
|
||||||
|
ExcludeDumpTimeSharedClasses excl;
|
||||||
|
_dumptime_table->iterate(&excl);
|
||||||
|
DEBUG_ONLY(_checked_excluded_classes = true;)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
|
||||||
|
assert(_checked_excluded_classes, "sanity");
|
||||||
|
assert(DumpSharedSpaces, "only when dumping");
|
||||||
|
return find_or_allocate_info_for(k)->_excluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
class IterateDumpTimeSharedClassTable : StackObj {
|
||||||
|
MetaspaceClosure *_it;
|
||||||
|
public:
|
||||||
|
IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {}
|
||||||
|
|
||||||
|
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||||||
|
if (!info._excluded) {
|
||||||
|
info.metaspace_pointers_do(_it);
|
||||||
|
}
|
||||||
|
return true; // keep on iterating
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
|
||||||
|
IterateDumpTimeSharedClassTable iter(it);
|
||||||
|
_dumptime_table->iterate(&iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
|
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
||||||
assert(DumpSharedSpaces, "called at dump time only");
|
assert(DumpSharedSpaces, "called at dump time only");
|
||||||
|
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
|
||||||
// Skip unsafe anonymous classes, which are not archived as they are not in
|
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
|
||||||
// dictionary (see assert_no_unsafe_anonymous_classes_in_dictionaries() in
|
|
||||||
// VM_PopulateDumpSharedSpace::doit()).
|
|
||||||
if (k->class_loader_data()->is_unsafe_anonymous()) {
|
|
||||||
return true; // unsafe anonymous classes are not archived, skip
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedDictionaryEntry* entry = ((SharedDictionary*)(k->class_loader_data()->dictionary()))->find_entry_for(k);
|
|
||||||
ResourceMark rm;
|
|
||||||
// Lambda classes are not archived and will be regenerated at runtime.
|
|
||||||
if (entry == NULL) {
|
|
||||||
guarantee(strstr(k->name()->as_C_string(), "Lambda$") != NULL,
|
|
||||||
"class should be in dictionary before being verified");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
entry->add_verification_constraint(name, from_name, from_field_is_protected,
|
|
||||||
from_is_array, from_is_object);
|
from_is_array, from_is_object);
|
||||||
if (entry->is_builtin()) {
|
if (is_builtin(k)) {
|
||||||
// For builtin class loaders, we can try to complete the verification check at dump time,
|
// For builtin class loaders, we can try to complete the verification check at dump time,
|
||||||
// because we can resolve all the constraint classes.
|
// because we can resolve all the constraint classes.
|
||||||
return false;
|
return false;
|
||||||
@ -783,135 +1101,54 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemDictionaryShared::finalize_verification_constraints_for(InstanceKlass* k) {
|
void DumpTimeSharedClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||||
if (!k->is_unsafe_anonymous()) {
|
|
||||||
SharedDictionaryEntry* entry = ((SharedDictionary*)(k->class_loader_data()->dictionary()))->find_entry_for(k);
|
|
||||||
entry->finalize_verification_constraints();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionaryShared::finalize_verification_constraints() {
|
|
||||||
MutexLocker mcld(ClassLoaderDataGraph_lock);
|
|
||||||
ClassLoaderDataGraph::dictionary_classes_do(finalize_verification_constraints_for);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
|
|
||||||
TRAPS) {
|
|
||||||
assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only");
|
|
||||||
SharedDictionaryEntry* entry = shared_dictionary()->find_entry_for(klass);
|
|
||||||
assert(entry != NULL, "call this only for shared classes");
|
|
||||||
entry->check_verification_constraints(klass, THREAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedDictionaryEntry* SharedDictionary::find_entry_for(InstanceKlass* klass) {
|
|
||||||
Symbol* class_name = klass->name();
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
int index = hash_to_index(hash);
|
|
||||||
|
|
||||||
for (SharedDictionaryEntry* entry = bucket(index);
|
|
||||||
entry != NULL;
|
|
||||||
entry = entry->next()) {
|
|
||||||
if (entry->hash() == hash && entry->literal() == klass) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SharedDictionaryEntry::add_verification_constraint(Symbol* name,
|
|
||||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
||||||
if (_verifier_constraints == NULL) {
|
if (_verifier_constraints == NULL) {
|
||||||
_verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<Symbol*>(8, true, mtClass);
|
_verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<DTConstraint>(4, true, mtClass);
|
||||||
}
|
}
|
||||||
if (_verifier_constraint_flags == NULL) {
|
if (_verifier_constraint_flags == NULL) {
|
||||||
_verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray<char>(4, true, mtClass);
|
_verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray<char>(4, true, mtClass);
|
||||||
}
|
}
|
||||||
GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
|
GrowableArray<DTConstraint>* vc_array = _verifier_constraints;
|
||||||
for (int i=0; i<vc_array->length(); i+= 2) {
|
for (int i = 0; i < vc_array->length(); i++) {
|
||||||
if (name == vc_array->at(i) &&
|
DTConstraint* p = vc_array->adr_at(i);
|
||||||
from_name == vc_array->at(i+1)) {
|
if (name == p->_name && from_name == p->_from_name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vc_array->append(name);
|
DTConstraint cons(name, from_name);
|
||||||
vc_array->append(from_name);
|
vc_array->append(cons);
|
||||||
|
|
||||||
GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
|
GrowableArray<char>* vcflags_array = _verifier_constraint_flags;
|
||||||
char c = 0;
|
char c = 0;
|
||||||
c |= from_field_is_protected ? FROM_FIELD_IS_PROTECTED : 0;
|
c |= from_field_is_protected ? SystemDictionaryShared::FROM_FIELD_IS_PROTECTED : 0;
|
||||||
c |= from_is_array ? FROM_IS_ARRAY : 0;
|
c |= from_is_array ? SystemDictionaryShared::FROM_IS_ARRAY : 0;
|
||||||
c |= from_is_object ? FROM_IS_OBJECT : 0;
|
c |= from_is_object ? SystemDictionaryShared::FROM_IS_OBJECT : 0;
|
||||||
vcflags_array->append(c);
|
vcflags_array->append(c);
|
||||||
|
|
||||||
if (log_is_enabled(Trace, cds, verification)) {
|
if (log_is_enabled(Trace, cds, verification)) {
|
||||||
ResourceMark rm;
|
ResourceMark rm;
|
||||||
log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s",
|
log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s",
|
||||||
instance_klass()->external_name(), from_name->as_klass_external_name(),
|
k->external_name(), from_name->as_klass_external_name(),
|
||||||
name->as_klass_external_name());
|
name->as_klass_external_name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int SharedDictionaryEntry::finalize_verification_constraints() {
|
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
|
||||||
assert(DumpSharedSpaces, "called at dump time only");
|
TRAPS) {
|
||||||
Thread* THREAD = Thread::current();
|
assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only");
|
||||||
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
|
RunTimeSharedClassInfo* record = RunTimeSharedClassInfo::get_for(klass);
|
||||||
GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
|
|
||||||
GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
|
|
||||||
|
|
||||||
if (vc_array != NULL) {
|
int length = record->_num_constraints;
|
||||||
if (log_is_enabled(Trace, cds, verification)) {
|
if (length > 0) {
|
||||||
ResourceMark rm;
|
|
||||||
log_trace(cds, verification)("finalize_verification_constraint: %s",
|
|
||||||
literal()->external_name());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the constraints from C_HEAP-alloced GrowableArrays to Metaspace-alloced
|
|
||||||
// Arrays
|
|
||||||
int size = 0;
|
|
||||||
{
|
|
||||||
// FIXME: change this to be done after relocation, so we can use symbol offset??
|
|
||||||
int length = vc_array->length();
|
|
||||||
Array<Symbol*>* out = MetadataFactory::new_array<Symbol*>(loader_data, length, 0, THREAD);
|
|
||||||
assert(out != NULL, "Dump time allocation failure would have aborted VM");
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
out->at_put(i, vc_array->at(i));
|
Symbol* name = record->get_constraint_name(i);
|
||||||
}
|
Symbol* from_name = record->get_constraint_from_name(i);
|
||||||
_verifier_constraints = out;
|
char c = record->get_constraint_flag(i);
|
||||||
size += out->size() * BytesPerWord;
|
|
||||||
delete vc_array;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int length = vcflags_array->length();
|
|
||||||
Array<char>* out = MetadataFactory::new_array<char>(loader_data, length, 0, THREAD);
|
|
||||||
assert(out != NULL, "Dump time allocation failure would have aborted VM");
|
|
||||||
for (int i=0; i<length; i++) {
|
|
||||||
out->at_put(i, vcflags_array->at(i));
|
|
||||||
}
|
|
||||||
_verifier_constraint_flags = out;
|
|
||||||
size += out->size() * BytesPerWord;
|
|
||||||
delete vcflags_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
bool from_field_is_protected = (c & SystemDictionaryShared::FROM_FIELD_IS_PROTECTED) ? true : false;
|
||||||
}
|
bool from_is_array = (c & SystemDictionaryShared::FROM_IS_ARRAY) ? true : false;
|
||||||
return 0;
|
bool from_is_object = (c & SystemDictionaryShared::FROM_IS_OBJECT) ? true : false;
|
||||||
}
|
|
||||||
|
|
||||||
void SharedDictionaryEntry::check_verification_constraints(InstanceKlass* klass, TRAPS) {
|
|
||||||
Array<Symbol*>* vc_array = (Array<Symbol*>*)_verifier_constraints;
|
|
||||||
Array<char>* vcflags_array = (Array<char>*)_verifier_constraint_flags;
|
|
||||||
|
|
||||||
if (vc_array != NULL) {
|
|
||||||
int length = vc_array->length();
|
|
||||||
for (int i=0; i<length; i+=2) {
|
|
||||||
Symbol* name = vc_array->at(i);
|
|
||||||
Symbol* from_name = vc_array->at(i+1);
|
|
||||||
char c = vcflags_array->at(i/2);
|
|
||||||
|
|
||||||
bool from_field_is_protected = (c & FROM_FIELD_IS_PROTECTED) ? true : false;
|
|
||||||
bool from_is_array = (c & FROM_IS_ARRAY) ? true : false;
|
|
||||||
bool from_is_object = (c & FROM_IS_OBJECT) ? true : false;
|
|
||||||
|
|
||||||
bool ok = VerificationType::resolve_and_check_assignability(klass, name,
|
bool ok = VerificationType::resolve_and_check_assignability(klass, name,
|
||||||
from_name, from_field_is_protected, from_is_array, from_is_object, CHECK);
|
from_name, from_field_is_protected, from_is_array, from_is_object, CHECK);
|
||||||
@ -930,132 +1167,100 @@ void SharedDictionaryEntry::check_verification_constraints(InstanceKlass* klass,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedDictionaryEntry::metaspace_pointers_do(MetaspaceClosure* it) {
|
class CopySharedClassInfoToArchive : StackObj {
|
||||||
it->push((Array<Symbol*>**)&_verifier_constraints);
|
CompactHashtableWriter* _writer;
|
||||||
it->push((Array<char>**)&_verifier_constraint_flags);
|
bool _is_builtin;
|
||||||
|
public:
|
||||||
|
CopySharedClassInfoToArchive(CompactHashtableWriter* writer, bool is_builtin)
|
||||||
|
: _writer(writer), _is_builtin(is_builtin) {}
|
||||||
|
|
||||||
|
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||||||
|
if (!info._excluded && info.is_builtin() == _is_builtin) {
|
||||||
|
size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints());
|
||||||
|
RunTimeSharedClassInfo* record =
|
||||||
|
(RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size);
|
||||||
|
record->init(info);
|
||||||
|
|
||||||
|
unsigned int hash = primitive_hash<Symbol*>(info._klass->name());
|
||||||
|
_writer->add(hash, MetaspaceShared::object_delta_u4(record));
|
||||||
|
|
||||||
|
// Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo*
|
||||||
|
RunTimeSharedClassInfo::set_for(info._klass, record);
|
||||||
|
}
|
||||||
|
return true; // keep on iterating
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin) {
|
||||||
|
CompactHashtableStats stats;
|
||||||
|
dictionary->reset();
|
||||||
|
int num_buckets = CompactHashtableWriter::default_num_buckets(_dumptime_table->count_of(is_builtin));
|
||||||
|
CompactHashtableWriter writer(num_buckets, &stats);
|
||||||
|
CopySharedClassInfoToArchive copy(&writer, is_builtin);
|
||||||
|
_dumptime_table->iterate(©);
|
||||||
|
writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SharedDictionary::add_non_builtin_klass(const Symbol* class_name,
|
void SystemDictionaryShared::write_to_archive() {
|
||||||
ClassLoaderData* loader_data,
|
_dumptime_table->update_counts();
|
||||||
InstanceKlass* klass) {
|
write_dictionary(&_builtin_dictionary, true);
|
||||||
|
write_dictionary(&_unregistered_dictionary, false);
|
||||||
assert(DumpSharedSpaces, "supported only when dumping");
|
|
||||||
assert(klass != NULL, "adding NULL klass");
|
|
||||||
assert(klass->name() == class_name, "sanity check on name");
|
|
||||||
assert(klass->shared_classpath_index() < 0,
|
|
||||||
"the shared classpath index should not be set for shared class loaded by the custom loaders");
|
|
||||||
|
|
||||||
// Add an entry for a non-builtin class.
|
|
||||||
// For a shared class for custom class loaders, SystemDictionary::resolve_or_null will
|
|
||||||
// not find this class, because is_builtin() is false.
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
int index = hash_to_index(hash);
|
|
||||||
|
|
||||||
for (SharedDictionaryEntry* entry = bucket(index);
|
|
||||||
entry != NULL;
|
|
||||||
entry = entry->next()) {
|
|
||||||
if (entry->hash() == hash) {
|
|
||||||
InstanceKlass* klass = entry->instance_klass();
|
|
||||||
if (klass->name() == class_name && klass->class_loader_data() == loader_data) {
|
|
||||||
// There is already a class defined with the same name
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(Dictionary::entry_size() >= sizeof(SharedDictionaryEntry), "must be big enough");
|
void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) {
|
||||||
SharedDictionaryEntry* entry = (SharedDictionaryEntry*)new_entry(hash, klass);
|
_builtin_dictionary.serialize_header(soc);
|
||||||
add_entry(index, entry);
|
_unregistered_dictionary.serialize_header(soc);
|
||||||
|
|
||||||
assert(entry->is_unregistered(), "sanity");
|
|
||||||
assert(!entry->is_builtin(), "sanity");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RunTimeSharedClassInfo*
|
||||||
//-----------------
|
SystemDictionaryShared::find_record(RunTimeSharedDictionary* dict, Symbol* name) {
|
||||||
// SharedDictionary
|
if (UseSharedSpaces) {
|
||||||
//-----------------
|
unsigned int hash = primitive_hash<Symbol*>(name);
|
||||||
|
return dict->lookup(name, hash, 0);
|
||||||
|
|
||||||
InstanceKlass* SharedDictionary::find_class_for_builtin_loader(const Symbol* name) const {
|
|
||||||
SharedDictionaryEntry* entry = get_entry_for_builtin_loader(name);
|
|
||||||
return entry != NULL ? entry->instance_klass() : (InstanceKlass*)NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceKlass* SharedDictionary::find_class_for_unregistered_loader(const Symbol* name,
|
|
||||||
int clsfile_size,
|
|
||||||
int clsfile_crc32) const {
|
|
||||||
|
|
||||||
const SharedDictionaryEntry* entry = get_entry_for_unregistered_loader(name,
|
|
||||||
clsfile_size,
|
|
||||||
clsfile_crc32);
|
|
||||||
return entry != NULL ? entry->instance_klass() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SharedDictionary::update_entry(InstanceKlass* klass, int id) {
|
|
||||||
assert(DumpSharedSpaces, "supported only when dumping");
|
|
||||||
Symbol* class_name = klass->name();
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
int index = hash_to_index(hash);
|
|
||||||
|
|
||||||
for (SharedDictionaryEntry* entry = bucket(index);
|
|
||||||
entry != NULL;
|
|
||||||
entry = entry->next()) {
|
|
||||||
if (entry->hash() == hash && entry->literal() == klass) {
|
|
||||||
entry->_id = id;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShouldNotReachHere();
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedDictionaryEntry* SharedDictionary::get_entry_for_builtin_loader(const Symbol* class_name) const {
|
|
||||||
assert(!DumpSharedSpaces, "supported only when at runtime");
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
const int index = hash_to_index(hash);
|
|
||||||
|
|
||||||
for (SharedDictionaryEntry* entry = bucket(index);
|
|
||||||
entry != NULL;
|
|
||||||
entry = entry->next()) {
|
|
||||||
if (entry->hash() == hash && entry->equals(class_name)) {
|
|
||||||
if (entry->is_builtin()) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedDictionaryEntry* SharedDictionary::get_entry_for_unregistered_loader(const Symbol* class_name,
|
|
||||||
int clsfile_size,
|
|
||||||
int clsfile_crc32) const {
|
|
||||||
assert(!DumpSharedSpaces, "supported only when at runtime");
|
|
||||||
unsigned int hash = compute_hash(class_name);
|
|
||||||
int index = hash_to_index(hash);
|
|
||||||
|
|
||||||
for (SharedDictionaryEntry* entry = bucket(index);
|
|
||||||
entry != NULL;
|
|
||||||
entry = entry->next()) {
|
|
||||||
if (entry->hash() == hash && entry->equals(class_name)) {
|
|
||||||
if (entry->is_unregistered()) {
|
|
||||||
if (clsfile_size == -1) {
|
|
||||||
// We're called from class_exists_for_unregistered_loader. At run time, we want to
|
|
||||||
// compute the CRC of a ClassFileStream only if there is an UNREGISTERED class
|
|
||||||
// with the matching name.
|
|
||||||
return entry;
|
|
||||||
} else {
|
} else {
|
||||||
// We're called from find_class_for_unregistered_loader
|
return NULL;
|
||||||
if (entry->_clsfile_size && clsfile_crc32 == entry->_clsfile_crc32) {
|
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There can be only 1 class with this name for unregistered loaders.
|
InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) {
|
||||||
|
const RunTimeSharedClassInfo* record = find_record(&_builtin_dictionary, name);
|
||||||
|
if (record) {
|
||||||
|
return record->_klass;
|
||||||
|
} else {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) {
|
||||||
|
assert(DumpSharedSpaces, "supported only when dumping");
|
||||||
|
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
|
||||||
|
info->_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedDictionaryPrinter : StackObj {
|
||||||
|
outputStream* _st;
|
||||||
|
int _index;
|
||||||
|
public:
|
||||||
|
SharedDictionaryPrinter(outputStream* st) : _st(st), _index(0) {}
|
||||||
|
|
||||||
|
void do_value(const RunTimeSharedClassInfo* record) {
|
||||||
|
ResourceMark rm;
|
||||||
|
_st->print_cr("%4d: %s", (_index++), record->_klass->external_name());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemDictionaryShared::print_on(outputStream* st) {
|
||||||
|
if (UseSharedSpaces) {
|
||||||
|
st->print_cr("Shared Dictionary");
|
||||||
|
SharedDictionaryPrinter p(st);
|
||||||
|
_builtin_dictionary.iterate(&p);
|
||||||
|
_unregistered_dictionary.iterate(&p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemDictionaryShared::print_table_statistics(outputStream* st) {
|
||||||
|
if (UseSharedSpaces) {
|
||||||
|
_builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary");
|
||||||
|
_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary");
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,12 @@
|
|||||||
Handling of the classes in the AppCDS archive
|
Handling of the classes in the AppCDS archive
|
||||||
|
|
||||||
To ensure safety and to simplify the implementation, archived classes are
|
To ensure safety and to simplify the implementation, archived classes are
|
||||||
"segregated" into several types. The following rules describe how they
|
"segregated" into 2 types. The following rules describe how they
|
||||||
are stored and looked up.
|
are stored and looked up.
|
||||||
|
|
||||||
[1] Category of archived classes
|
[1] Category of archived classes
|
||||||
|
|
||||||
There are 3 disjoint groups of classes stored in the AppCDS archive. They are
|
There are 2 disjoint groups of classes stored in the AppCDS archive:
|
||||||
categorized as by their SharedDictionaryEntry::loader_type()
|
|
||||||
|
|
||||||
BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP
|
BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP
|
||||||
loaders.
|
loaders.
|
||||||
@ -83,112 +82,39 @@
|
|||||||
Bar id: 3 super: 0 interfaces: 1 source: /foo.jar
|
Bar id: 3 super: 0 interfaces: 1 source: /foo.jar
|
||||||
|
|
||||||
|
|
||||||
[3] Identifying the loader_type of archived classes in the shared dictionary
|
[3] Identifying the category of archived classes
|
||||||
|
|
||||||
Each archived Klass* C is associated with a SharedDictionaryEntry* E
|
|
||||||
|
|
||||||
BUILTIN: (C->shared_classpath_index() >= 0)
|
BUILTIN: (C->shared_classpath_index() >= 0)
|
||||||
UNREGISTERED: (C->shared_classpath_index() < 0)
|
UNREGISTERED: (C->shared_classpath_index() == UNREGISTERED_INDEX (-9999))
|
||||||
|
|
||||||
[4] Lookup of archived classes at run time:
|
[4] Lookup of archived classes at run time:
|
||||||
|
|
||||||
(a) BUILTIN loaders:
|
(a) BUILTIN loaders:
|
||||||
|
|
||||||
Search the shared directory for a BUILTIN class with a matching name.
|
search _builtin_dictionary
|
||||||
|
|
||||||
(b) UNREGISTERED loaders:
|
(b) UNREGISTERED loaders:
|
||||||
|
|
||||||
The search originates with SystemDictionaryShared::lookup_from_stream().
|
search _unregistered_dictionary for an entry that matches the
|
||||||
|
(name, clsfile_len, clsfile_crc32).
|
||||||
Search the shared directory for a UNREGISTERED class with a matching
|
|
||||||
(name, clsfile_len, clsfile_crc32) tuple.
|
|
||||||
|
|
||||||
===============================================================================*/
|
===============================================================================*/
|
||||||
#define UNREGISTERED_INDEX -9999
|
#define UNREGISTERED_INDEX -9999
|
||||||
|
|
||||||
class ClassFileStream;
|
class ClassFileStream;
|
||||||
|
class DumpTimeSharedClassInfo;
|
||||||
|
class DumpTimeSharedClassTable;
|
||||||
|
class RunTimeSharedClassInfo;
|
||||||
|
class RunTimeSharedDictionary;
|
||||||
|
|
||||||
// Archived classes need extra information not needed by traditionally loaded classes.
|
class SystemDictionaryShared: public SystemDictionary {
|
||||||
// To keep footprint small, we add these in the dictionary entry instead of the InstanceKlass.
|
|
||||||
class SharedDictionaryEntry : public DictionaryEntry {
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum LoaderType {
|
|
||||||
LT_BUILTIN,
|
|
||||||
LT_UNREGISTERED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
FROM_FIELD_IS_PROTECTED = 1 << 0,
|
FROM_FIELD_IS_PROTECTED = 1 << 0,
|
||||||
FROM_IS_ARRAY = 1 << 1,
|
FROM_IS_ARRAY = 1 << 1,
|
||||||
FROM_IS_OBJECT = 1 << 2
|
FROM_IS_OBJECT = 1 << 2
|
||||||
};
|
};
|
||||||
|
|
||||||
int _id;
|
|
||||||
int _clsfile_size;
|
|
||||||
int _clsfile_crc32;
|
|
||||||
void* _verifier_constraints; // FIXME - use a union here to avoid type casting??
|
|
||||||
void* _verifier_constraint_flags;
|
|
||||||
|
|
||||||
// See "Identifying the loader_type of archived classes" comments above.
|
|
||||||
LoaderType loader_type() const {
|
|
||||||
InstanceKlass* k = instance_klass();
|
|
||||||
|
|
||||||
if ((k->shared_classpath_index() != UNREGISTERED_INDEX)) {
|
|
||||||
return LT_BUILTIN;
|
|
||||||
} else {
|
|
||||||
return LT_UNREGISTERED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedDictionaryEntry* next() {
|
|
||||||
return (SharedDictionaryEntry*)(DictionaryEntry::next());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_builtin() const {
|
|
||||||
return loader_type() == LT_BUILTIN;
|
|
||||||
}
|
|
||||||
bool is_unregistered() const {
|
|
||||||
return loader_type() == LT_UNREGISTERED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_verification_constraint(Symbol* name,
|
|
||||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
|
|
||||||
int finalize_verification_constraints();
|
|
||||||
void check_verification_constraints(InstanceKlass* klass, TRAPS);
|
|
||||||
void metaspace_pointers_do(MetaspaceClosure* it) NOT_CDS_RETURN;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SharedDictionary : public Dictionary {
|
|
||||||
SharedDictionaryEntry* get_entry_for_builtin_loader(const Symbol* name) const;
|
|
||||||
SharedDictionaryEntry* get_entry_for_unregistered_loader(const Symbol* name,
|
|
||||||
int clsfile_size,
|
|
||||||
int clsfile_crc32) const;
|
|
||||||
|
|
||||||
// Convenience functions
|
|
||||||
SharedDictionaryEntry* bucket(int index) const {
|
|
||||||
return (SharedDictionaryEntry*)(Dictionary::bucket(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
SharedDictionaryEntry* find_entry_for(InstanceKlass* klass);
|
|
||||||
|
|
||||||
bool add_non_builtin_klass(const Symbol* class_name,
|
|
||||||
ClassLoaderData* loader_data,
|
|
||||||
InstanceKlass* obj);
|
|
||||||
|
|
||||||
void update_entry(InstanceKlass* klass, int id);
|
|
||||||
|
|
||||||
InstanceKlass* find_class_for_builtin_loader(const Symbol* name) const;
|
|
||||||
InstanceKlass* find_class_for_unregistered_loader(const Symbol* name,
|
|
||||||
int clsfile_size,
|
|
||||||
int clsfile_crc32) const;
|
|
||||||
bool class_exists_for_unregistered_loader(const Symbol* name) {
|
|
||||||
return (get_entry_for_unregistered_loader(name, -1, -1) != NULL);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SystemDictionaryShared: public SystemDictionary {
|
|
||||||
private:
|
private:
|
||||||
// These _shared_xxxs arrays are used to initialize the java.lang.Package and
|
// These _shared_xxxs arrays are used to initialize the java.lang.Package and
|
||||||
// java.security.ProtectionDomain objects associated with each shared class.
|
// java.security.ProtectionDomain objects associated with each shared class.
|
||||||
@ -282,8 +208,17 @@ private:
|
|||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
static void finalize_verification_constraints_for(InstanceKlass* k);
|
static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k);
|
||||||
|
static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin);
|
||||||
|
static bool is_jfr_event_class(InstanceKlass *k);
|
||||||
|
static void warn_excluded(InstanceKlass* k, const char* reason);
|
||||||
|
|
||||||
|
DEBUG_ONLY(static bool _checked_excluded_classes;)
|
||||||
public:
|
public:
|
||||||
|
static InstanceKlass* find_builtin_class(Symbol* class_name);
|
||||||
|
|
||||||
|
static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dict, Symbol* name);
|
||||||
|
|
||||||
// Called by PLATFORM/APP loader only
|
// Called by PLATFORM/APP loader only
|
||||||
static InstanceKlass* find_or_load_shared_class(Symbol* class_name,
|
static InstanceKlass* find_or_load_shared_class(Symbol* class_name,
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
@ -311,8 +246,7 @@ public:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool add_non_builtin_klass(Symbol* class_name, ClassLoaderData* loader_data,
|
static bool add_unregistered_class(InstanceKlass* k, TRAPS);
|
||||||
InstanceKlass* k, TRAPS);
|
|
||||||
static InstanceKlass* dump_time_resolve_super_or_fail(Symbol* child_name,
|
static InstanceKlass* dump_time_resolve_super_or_fail(Symbol* child_name,
|
||||||
Symbol* class_name,
|
Symbol* class_name,
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
@ -320,36 +254,17 @@ public:
|
|||||||
bool is_superclass,
|
bool is_superclass,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
static size_t dictionary_entry_size() {
|
static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||||||
return (DumpSharedSpaces) ? sizeof(SharedDictionaryEntry) : sizeof(DictionaryEntry);
|
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||||||
}
|
|
||||||
static void init_shared_dictionary_entry(InstanceKlass* k, DictionaryEntry* entry) NOT_CDS_RETURN;
|
static Dictionary* boot_loader_dictionary() {
|
||||||
static bool is_builtin(DictionaryEntry* ent) {
|
return ClassLoaderData::the_null_class_loader_data()->dictionary();
|
||||||
// Can't use virtual function is_builtin because DictionaryEntry doesn't initialize
|
|
||||||
// vtable because it's not constructed properly.
|
|
||||||
SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent;
|
|
||||||
return entry->is_builtin();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For convenient access to the SharedDictionaryEntry's of the archived classes.
|
|
||||||
static SharedDictionary* shared_dictionary() {
|
|
||||||
assert(!DumpSharedSpaces, "not for dumping");
|
|
||||||
return (SharedDictionary*)SystemDictionary::shared_dictionary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SharedDictionary* boot_loader_dictionary() {
|
|
||||||
return (SharedDictionary*)ClassLoaderData::the_null_class_loader_data()->dictionary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_shared_entry(InstanceKlass* klass, int id) {
|
|
||||||
assert(DumpSharedSpaces, "sanity");
|
|
||||||
assert((SharedDictionary*)(klass->class_loader_data()->dictionary()) != NULL, "sanity");
|
|
||||||
((SharedDictionary*)(klass->class_loader_data()->dictionary()))->update_entry(klass, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void update_shared_entry(InstanceKlass* klass, int id);
|
||||||
static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs);
|
static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs);
|
||||||
|
|
||||||
static InstanceKlass* lookup_from_stream(const Symbol* class_name,
|
static InstanceKlass* lookup_from_stream(Symbol* class_name,
|
||||||
Handle class_loader,
|
Handle class_loader,
|
||||||
Handle protection_domain,
|
Handle protection_domain,
|
||||||
const ClassFileStream* st,
|
const ClassFileStream* st,
|
||||||
@ -366,9 +281,23 @@ public:
|
|||||||
static bool add_verification_constraint(InstanceKlass* k, Symbol* name,
|
static bool add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||||
Symbol* from_name, bool from_field_is_protected,
|
Symbol* from_name, bool from_field_is_protected,
|
||||||
bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
|
bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
|
||||||
static void finalize_verification_constraints() NOT_CDS_RETURN;
|
|
||||||
static void check_verification_constraints(InstanceKlass* klass,
|
static void check_verification_constraints(InstanceKlass* klass,
|
||||||
TRAPS) NOT_CDS_RETURN;
|
TRAPS) NOT_CDS_RETURN;
|
||||||
|
static bool is_builtin(InstanceKlass* k) {
|
||||||
|
return (k->shared_classpath_index() != UNREGISTERED_INDEX);
|
||||||
|
}
|
||||||
|
static bool should_be_excluded(InstanceKlass* k);
|
||||||
|
static void check_excluded_classes();
|
||||||
|
static void validate_before_archiving(InstanceKlass* k);
|
||||||
|
static bool is_excluded_class(InstanceKlass* k);
|
||||||
|
static void dumptime_classes_do(class MetaspaceClosure* it);
|
||||||
|
static void write_to_archive();
|
||||||
|
static void serialize_dictionary_headers(class SerializeClosure* soc);
|
||||||
|
static void print() { return print_on(tty); }
|
||||||
|
static void print_on(outputStream* st) NOT_CDS_RETURN;
|
||||||
|
static void print_table_statistics(outputStream* st) NOT_CDS_RETURN;
|
||||||
|
|
||||||
|
DEBUG_ONLY(static bool checked_excluded_classes() {return _checked_excluded_classes;})
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||||||
|
@ -393,6 +393,10 @@ class BufferBlob: public RuntimeBlob {
|
|||||||
BufferBlob(const char* name, int size);
|
BufferBlob(const char* name, int size);
|
||||||
BufferBlob(const char* name, int size, CodeBuffer* cb);
|
BufferBlob(const char* name, int size, CodeBuffer* cb);
|
||||||
|
|
||||||
|
// This ordinary operator delete is needed even though not used, so the
|
||||||
|
// below two-argument operator delete will be treated as a placement
|
||||||
|
// delete rather than an ordinary sized delete; see C++14 3.7.4.2/p2.
|
||||||
|
void operator delete(void* p);
|
||||||
void* operator new(size_t s, unsigned size) throw();
|
void* operator new(size_t s, unsigned size) throw();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -476,6 +480,10 @@ class RuntimeStub: public RuntimeBlob {
|
|||||||
bool caller_must_gc_arguments
|
bool caller_must_gc_arguments
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This ordinary operator delete is needed even though not used, so the
|
||||||
|
// below two-argument operator delete will be treated as a placement
|
||||||
|
// delete rather than an ordinary sized delete; see C++14 3.7.4.2/p2.
|
||||||
|
void operator delete(void* p);
|
||||||
void* operator new(size_t s, unsigned size) throw();
|
void* operator new(size_t s, unsigned size) throw();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -511,6 +519,10 @@ class SingletonBlob: public RuntimeBlob {
|
|||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// This ordinary operator delete is needed even though not used, so the
|
||||||
|
// below two-argument operator delete will be treated as a placement
|
||||||
|
// delete rather than an ordinary sized delete; see C++14 3.7.4.2/p2.
|
||||||
|
void operator delete(void* p);
|
||||||
void* operator new(size_t s, unsigned size) throw();
|
void* operator new(size_t s, unsigned size) throw();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -1655,7 +1655,7 @@ void G1ConcurrentMark::weak_refs_work(bool clear_all_soft_refs) {
|
|||||||
// Unload Klasses, String, Code Cache, etc.
|
// Unload Klasses, String, Code Cache, etc.
|
||||||
if (ClassUnloadingWithConcurrentMark) {
|
if (ClassUnloadingWithConcurrentMark) {
|
||||||
GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
|
GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
|
||||||
bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm, false /* Defer cleaning */);
|
bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm);
|
||||||
_g1h->complete_cleaning(&g1_is_alive, purged_classes);
|
_g1h->complete_cleaning(&g1_is_alive, purged_classes);
|
||||||
} else {
|
} else {
|
||||||
GCTraceTime(Debug, gc, phases) debug("Cleanup", _gc_timer_cm);
|
GCTraceTime(Debug, gc, phases) debug("Cleanup", _gc_timer_cm);
|
||||||
|
@ -244,6 +244,7 @@ class ClassLoaderData;
|
|||||||
class MetaspaceClosure;
|
class MetaspaceClosure;
|
||||||
|
|
||||||
class MetaspaceObj {
|
class MetaspaceObj {
|
||||||
|
friend class VMStructs;
|
||||||
// When CDS is enabled, all shared metaspace objects are mapped
|
// When CDS is enabled, all shared metaspace objects are mapped
|
||||||
// into a single contiguous memory block, so we can use these
|
// into a single contiguous memory block, so we can use these
|
||||||
// two pointers to quickly determine if something is in the
|
// two pointers to quickly determine if something is in the
|
||||||
|
@ -209,6 +209,7 @@ void FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
|||||||
_verify_local = BytecodeVerificationLocal;
|
_verify_local = BytecodeVerificationLocal;
|
||||||
_verify_remote = BytecodeVerificationRemote;
|
_verify_remote = BytecodeVerificationRemote;
|
||||||
_has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes();
|
_has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes();
|
||||||
|
_shared_base_address = SharedBaseAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS) {
|
void SharedClassPathEntry::init(const char* name, bool is_modules_image, TRAPS) {
|
||||||
@ -533,6 +534,7 @@ bool FileMapInfo::init_from_file(int fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_file_offset += (long)n;
|
_file_offset += (long)n;
|
||||||
|
SharedBaseAddress = _header->_shared_base_address;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,7 +668,8 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
|
|||||||
// +-- gap
|
// +-- gap
|
||||||
size_t FileMapInfo::write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
|
size_t FileMapInfo::write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
|
||||||
GrowableArray<ArchiveHeapOopmapInfo> *oopmaps,
|
GrowableArray<ArchiveHeapOopmapInfo> *oopmaps,
|
||||||
int first_region_id, int max_num_regions) {
|
int first_region_id, int max_num_regions,
|
||||||
|
bool print_log) {
|
||||||
assert(max_num_regions <= 2, "Only support maximum 2 memory regions");
|
assert(max_num_regions <= 2, "Only support maximum 2 memory regions");
|
||||||
|
|
||||||
int arr_len = heap_mem == NULL ? 0 : heap_mem->length();
|
int arr_len = heap_mem == NULL ? 0 : heap_mem->length();
|
||||||
@ -687,8 +690,10 @@ size_t FileMapInfo::write_archive_heap_regions(GrowableArray<MemRegion> *heap_me
|
|||||||
total_size += size;
|
total_size += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (print_log) {
|
||||||
log_info(cds)("Archive heap region %d " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes",
|
log_info(cds)("Archive heap region %d " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes",
|
||||||
i, p2i(start), p2i(start + size), size);
|
i, p2i(start), p2i(start + size), size);
|
||||||
|
}
|
||||||
write_region(i, start, size, false, false);
|
write_region(i, start, size, false, false);
|
||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
space_at(i)->_oopmap = oopmaps->at(arr_idx)._oopmap;
|
space_at(i)->_oopmap = oopmaps->at(arr_idx)._oopmap;
|
||||||
|
@ -150,6 +150,7 @@ struct FileMapHeader : public CDSFileMapHeaderBase {
|
|||||||
bool _verify_local; // BytecodeVerificationLocal setting
|
bool _verify_local; // BytecodeVerificationLocal setting
|
||||||
bool _verify_remote; // BytecodeVerificationRemote setting
|
bool _verify_remote; // BytecodeVerificationRemote setting
|
||||||
bool _has_platform_or_app_classes; // Archive contains app classes
|
bool _has_platform_or_app_classes; // Archive contains app classes
|
||||||
|
size_t _shared_base_address; // SharedBaseAddress used at dump time
|
||||||
|
|
||||||
void set_has_platform_or_app_classes(bool v) {
|
void set_has_platform_or_app_classes(bool v) {
|
||||||
_has_platform_or_app_classes = v;
|
_has_platform_or_app_classes = v;
|
||||||
@ -263,7 +264,8 @@ public:
|
|||||||
bool read_only, bool allow_exec);
|
bool read_only, bool allow_exec);
|
||||||
size_t write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
|
size_t write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
|
||||||
GrowableArray<ArchiveHeapOopmapInfo> *oopmaps,
|
GrowableArray<ArchiveHeapOopmapInfo> *oopmaps,
|
||||||
int first_region_id, int max_num_regions);
|
int first_region_id, int max_num_regions,
|
||||||
|
bool print_log);
|
||||||
void write_bytes(const void* buffer, size_t count);
|
void write_bytes(const void* buffer, size_t count);
|
||||||
void write_bytes_aligned(const void* buffer, size_t count);
|
void write_bytes_aligned(const void* buffer, size_t count);
|
||||||
char* map_region(int i, char** top_ret);
|
char* map_region(int i, char** top_ret);
|
||||||
|
@ -391,9 +391,7 @@ struct CopyKlassSubGraphInfoToArchive : StackObj {
|
|||||||
record->init(&info);
|
record->init(&info);
|
||||||
|
|
||||||
unsigned int hash = primitive_hash<Klass*>(klass);
|
unsigned int hash = primitive_hash<Klass*>(klass);
|
||||||
uintx deltax = MetaspaceShared::object_delta(record);
|
u4 delta = MetaspaceShared::object_delta_u4(record);
|
||||||
guarantee(deltax <= MAX_SHARED_DELTA, "must not be");
|
|
||||||
u4 delta = u4(deltax);
|
|
||||||
_writer->add(hash, delta);
|
_writer->add(hash, delta);
|
||||||
}
|
}
|
||||||
return true; // keep on iterating
|
return true; // keep on iterating
|
||||||
@ -417,7 +415,7 @@ void HeapShared::write_subgraph_info_table() {
|
|||||||
int num_buckets = CompactHashtableWriter::default_num_buckets(d_table->_count);
|
int num_buckets = CompactHashtableWriter::default_num_buckets(d_table->_count);
|
||||||
CompactHashtableWriter writer(num_buckets, &stats);
|
CompactHashtableWriter writer(num_buckets, &stats);
|
||||||
CopyKlassSubGraphInfoToArchive copy(&writer);
|
CopyKlassSubGraphInfoToArchive copy(&writer);
|
||||||
_dump_time_subgraph_info_table->iterate(©);
|
d_table->iterate(©);
|
||||||
|
|
||||||
writer.dump(&_run_time_subgraph_info_table, "subgraphs");
|
writer.dump(&_run_time_subgraph_info_table, "subgraphs");
|
||||||
}
|
}
|
||||||
@ -433,7 +431,7 @@ void HeapShared::initialize_from_archived_subgraph(Klass* k) {
|
|||||||
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
|
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
|
||||||
|
|
||||||
unsigned int hash = primitive_hash<Klass*>(k);
|
unsigned int hash = primitive_hash<Klass*>(k);
|
||||||
ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0);
|
const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0);
|
||||||
|
|
||||||
// Initialize from archived data. Currently this is done only
|
// Initialize from archived data. Currently this is done only
|
||||||
// during VM initialization time. No lock is needed.
|
// during VM initialization time. No lock is needed.
|
||||||
|
@ -109,9 +109,9 @@ class ArchivedKlassSubGraphInfoRecord {
|
|||||||
ArchivedKlassSubGraphInfoRecord() :
|
ArchivedKlassSubGraphInfoRecord() :
|
||||||
_k(NULL), _entry_field_records(NULL), _subgraph_object_klasses(NULL) {}
|
_k(NULL), _entry_field_records(NULL), _subgraph_object_klasses(NULL) {}
|
||||||
void init(KlassSubGraphInfo* info);
|
void init(KlassSubGraphInfo* info);
|
||||||
Klass* klass() { return _k; }
|
Klass* klass() const { return _k; }
|
||||||
Array<juint>* entry_field_records() { return _entry_field_records; }
|
Array<juint>* entry_field_records() const { return _entry_field_records; }
|
||||||
Array<Klass*>* subgraph_object_klasses() { return _subgraph_object_klasses; }
|
Array<Klass*>* subgraph_object_klasses() const { return _subgraph_object_klasses; }
|
||||||
};
|
};
|
||||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||||
|
|
||||||
@ -154,18 +154,16 @@ class HeapShared: AllStatic {
|
|||||||
int _count;
|
int _count;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline static ArchivedKlassSubGraphInfoRecord* read_record_from_compact_hashtable(address base_address, u4 offset) {
|
public: // solaris compiler wants this for RunTimeKlassSubGraphInfoTable
|
||||||
return (ArchivedKlassSubGraphInfoRecord*)(base_address + offset);
|
inline static bool record_equals_compact_hashtable_entry(
|
||||||
}
|
const ArchivedKlassSubGraphInfoRecord* value, const Klass* key, int len_unused) {
|
||||||
|
|
||||||
inline static bool record_equals_compact_hashtable_entry(ArchivedKlassSubGraphInfoRecord* value, const Klass* key, int len_unused) {
|
|
||||||
return (value->klass() == key);
|
return (value->klass() == key);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef CompactHashtable<
|
private:
|
||||||
|
typedef OffsetCompactHashtable<
|
||||||
const Klass*,
|
const Klass*,
|
||||||
ArchivedKlassSubGraphInfoRecord*,
|
const ArchivedKlassSubGraphInfoRecord*,
|
||||||
read_record_from_compact_hashtable,
|
|
||||||
record_equals_compact_hashtable_entry
|
record_equals_compact_hashtable_entry
|
||||||
> RunTimeKlassSubGraphInfoTable;
|
> RunTimeKlassSubGraphInfoTable;
|
||||||
|
|
||||||
|
@ -64,7 +64,6 @@
|
|||||||
#include "utilities/align.hpp"
|
#include "utilities/align.hpp"
|
||||||
#include "utilities/bitMap.hpp"
|
#include "utilities/bitMap.hpp"
|
||||||
#include "utilities/defaultStream.hpp"
|
#include "utilities/defaultStream.hpp"
|
||||||
#include "utilities/hashtable.inline.hpp"
|
|
||||||
#if INCLUDE_G1GC
|
#if INCLUDE_G1GC
|
||||||
#include "gc/g1/g1CollectedHeap.hpp"
|
#include "gc/g1/g1CollectedHeap.hpp"
|
||||||
#endif
|
#endif
|
||||||
@ -124,6 +123,15 @@ private:
|
|||||||
MetaspaceShared::report_out_of_space(_name, newtop - _top);
|
MetaspaceShared::report_out_of_space(_name, newtop - _top);
|
||||||
ShouldNotReachHere();
|
ShouldNotReachHere();
|
||||||
}
|
}
|
||||||
|
uintx delta = MetaspaceShared::object_delta_uintx(newtop);
|
||||||
|
if (delta > MAX_SHARED_DELTA) {
|
||||||
|
// This is just a sanity check and should not appear in any real world usage. This
|
||||||
|
// happens only if you allocate more than 2GB of shared objects and would require
|
||||||
|
// millions of shared classes.
|
||||||
|
vm_exit_during_initialization("Out of memory in the CDS archive",
|
||||||
|
"Please reduce the number of shared classes.");
|
||||||
|
}
|
||||||
|
|
||||||
MetaspaceShared::commit_shared_space_to(newtop);
|
MetaspaceShared::commit_shared_space_to(newtop);
|
||||||
_top = newtop;
|
_top = newtop;
|
||||||
return _top;
|
return _top;
|
||||||
@ -323,6 +331,7 @@ void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_mc_region.init(&_shared_rs);
|
_mc_region.init(&_shared_rs);
|
||||||
|
SharedBaseAddress = (size_t)_shared_rs.base();
|
||||||
tty->print_cr("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT,
|
tty->print_cr("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT,
|
||||||
_shared_rs.size(), p2i(_shared_rs.base()));
|
_shared_rs.size(), p2i(_shared_rs.base()));
|
||||||
}
|
}
|
||||||
@ -338,12 +347,6 @@ void MetaspaceShared::post_initialize(TRAPS) {
|
|||||||
ClassLoaderExt::init_app_module_paths_start_index(header->_app_module_paths_start_index);
|
ClassLoaderExt::init_app_module_paths_start_index(header->_app_module_paths_start_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DumpSharedSpaces) {
|
|
||||||
if (SharedArchiveConfigFile) {
|
|
||||||
read_extra_data(SharedArchiveConfigFile, THREAD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaspaceShared::read_extra_data(const char* filename, TRAPS) {
|
void MetaspaceShared::read_extra_data(const char* filename, TRAPS) {
|
||||||
@ -422,6 +425,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) {
|
|||||||
SymbolTable::serialize_shared_table_header(soc);
|
SymbolTable::serialize_shared_table_header(soc);
|
||||||
StringTable::serialize_shared_table_header(soc);
|
StringTable::serialize_shared_table_header(soc);
|
||||||
HeapShared::serialize_subgraph_info_table_header(soc);
|
HeapShared::serialize_subgraph_info_table_header(soc);
|
||||||
|
SystemDictionaryShared::serialize_dictionary_headers(soc);
|
||||||
|
|
||||||
JavaClasses::serialize_offsets(soc);
|
JavaClasses::serialize_offsets(soc);
|
||||||
InstanceMirrorKlass::serialize_offsets(soc);
|
InstanceMirrorKlass::serialize_offsets(soc);
|
||||||
@ -470,14 +474,12 @@ static void collect_array_classes(Klass* k) {
|
|||||||
|
|
||||||
class CollectClassesClosure : public KlassClosure {
|
class CollectClassesClosure : public KlassClosure {
|
||||||
void do_klass(Klass* k) {
|
void do_klass(Klass* k) {
|
||||||
if (!(k->is_instance_klass() && InstanceKlass::cast(k)->is_in_error_state())) {
|
if (k->is_instance_klass() &&
|
||||||
if (k->is_instance_klass() && InstanceKlass::cast(k)->signers() != NULL) {
|
SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(k))) {
|
||||||
// Mark any class with signers and don't add to the _global_klass_objects
|
// Don't add to the _global_klass_objects
|
||||||
k->set_has_signer_and_not_archived();
|
|
||||||
} else {
|
} else {
|
||||||
_global_klass_objects->append_if_missing(k);
|
_global_klass_objects->append_if_missing(k);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (k->is_array_klass()) {
|
if (k->is_array_klass()) {
|
||||||
// Add in the array classes too
|
// Add in the array classes too
|
||||||
ArrayKlass* ak = ArrayKlass::cast(k);
|
ArrayKlass* ak = ArrayKlass::cast(k);
|
||||||
@ -583,16 +585,6 @@ static void relocate_cached_class_file() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NOT_PRODUCT(
|
|
||||||
static void assert_not_unsafe_anonymous_class(InstanceKlass* k) {
|
|
||||||
assert(!(k->is_unsafe_anonymous()), "cannot archive unsafe anonymous classes");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsafe anonymous classes are not stored inside any dictionaries.
|
|
||||||
static void assert_no_unsafe_anonymous_classes_in_dictionaries() {
|
|
||||||
ClassLoaderDataGraph::dictionary_classes_do(assert_not_unsafe_anonymous_class);
|
|
||||||
})
|
|
||||||
|
|
||||||
// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables.
|
// Objects of the Metadata types (such as Klass and ConstantPool) have C++ vtables.
|
||||||
// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.)
|
// (In GCC this is the field <Type>::_vptr, i.e., first word in the object.)
|
||||||
//
|
//
|
||||||
@ -1129,6 +1121,17 @@ public:
|
|||||||
newtop = _ro_region.top();
|
newtop = _ro_region.top();
|
||||||
} else {
|
} else {
|
||||||
oldtop = _rw_region.top();
|
oldtop = _rw_region.top();
|
||||||
|
if (ref->msotype() == MetaspaceObj::ClassType) {
|
||||||
|
// Save a pointer immediate in front of an InstanceKlass, so
|
||||||
|
// we can do a quick lookup from InstanceKlass* -> RunTimeSharedClassInfo*
|
||||||
|
// without building another hashtable. See RunTimeSharedClassInfo::get_for()
|
||||||
|
// in systemDictionaryShared.cpp.
|
||||||
|
Klass* klass = (Klass*)obj;
|
||||||
|
if (klass->is_instance_klass()) {
|
||||||
|
SystemDictionaryShared::validate_before_archiving(InstanceKlass::cast(klass));
|
||||||
|
_rw_region.allocate(sizeof(address), BytesPerWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
p = _rw_region.allocate(bytes, alignment);
|
p = _rw_region.allocate(bytes, alignment);
|
||||||
newtop = _rw_region.top();
|
newtop = _rw_region.top();
|
||||||
}
|
}
|
||||||
@ -1138,16 +1141,6 @@ public:
|
|||||||
assert(isnew, "must be");
|
assert(isnew, "must be");
|
||||||
|
|
||||||
_alloc_stats->record(ref->msotype(), int(newtop - oldtop), read_only);
|
_alloc_stats->record(ref->msotype(), int(newtop - oldtop), read_only);
|
||||||
if (ref->msotype() == MetaspaceObj::SymbolType) {
|
|
||||||
uintx delta = MetaspaceShared::object_delta(p);
|
|
||||||
if (delta > MAX_SHARED_DELTA) {
|
|
||||||
// This is just a sanity check and should not appear in any real world usage. This
|
|
||||||
// happens only if you allocate more than 2GB of Symbols and would require
|
|
||||||
// millions of shared classes.
|
|
||||||
vm_exit_during_initialization("Too many Symbols in the CDS archive",
|
|
||||||
"Please reduce the number of shared classes.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static address get_new_loc(MetaspaceClosure::Ref* ref) {
|
static address get_new_loc(MetaspaceClosure::Ref* ref) {
|
||||||
@ -1287,7 +1280,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileMapInfo::metaspace_pointers_do(it);
|
FileMapInfo::metaspace_pointers_do(it);
|
||||||
SystemDictionary::classes_do(it);
|
SystemDictionaryShared::dumptime_classes_do(it);
|
||||||
Universe::metaspace_pointers_do(it);
|
Universe::metaspace_pointers_do(it);
|
||||||
SymbolTable::metaspace_pointers_do(it);
|
SymbolTable::metaspace_pointers_do(it);
|
||||||
vmSymbols::metaspace_pointers_do(it);
|
vmSymbols::metaspace_pointers_do(it);
|
||||||
@ -1321,9 +1314,6 @@ void VM_PopulateDumpSharedSpace::dump_symbols() {
|
|||||||
|
|
||||||
char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
||||||
ArchiveCompactor::OtherROAllocMark mark;
|
ArchiveCompactor::OtherROAllocMark mark;
|
||||||
// Reorder the system dictionary. Moving the symbols affects
|
|
||||||
// how the hash table indices are calculated.
|
|
||||||
SystemDictionary::reorder_dictionary_for_sharing();
|
|
||||||
|
|
||||||
tty->print("Removing java_mirror ... ");
|
tty->print("Removing java_mirror ... ");
|
||||||
if (!HeapShared::is_heap_object_archiving_allowed()) {
|
if (!HeapShared::is_heap_object_archiving_allowed()) {
|
||||||
@ -1331,15 +1321,10 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
|||||||
}
|
}
|
||||||
remove_java_mirror_in_classes();
|
remove_java_mirror_in_classes();
|
||||||
tty->print_cr("done. ");
|
tty->print_cr("done. ");
|
||||||
NOT_PRODUCT(SystemDictionary::verify();)
|
|
||||||
|
|
||||||
size_t buckets_bytes = SystemDictionary::count_bytes_for_buckets();
|
SystemDictionaryShared::write_to_archive();
|
||||||
char* buckets_top = _ro_region.allocate(buckets_bytes, sizeof(intptr_t));
|
|
||||||
SystemDictionary::copy_buckets(buckets_top, _ro_region.top());
|
|
||||||
|
|
||||||
size_t table_bytes = SystemDictionary::count_bytes_for_table();
|
char* start = _ro_region.top();
|
||||||
char* table_top = _ro_region.allocate(table_bytes, sizeof(intptr_t));
|
|
||||||
SystemDictionary::copy_table(table_top, _ro_region.top());
|
|
||||||
|
|
||||||
// Write the other data to the output array.
|
// Write the other data to the output array.
|
||||||
WriteClosure wc(&_ro_region);
|
WriteClosure wc(&_ro_region);
|
||||||
@ -1348,7 +1333,7 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
|
|||||||
// Write the bitmaps for patching the archive heap regions
|
// Write the bitmaps for patching the archive heap regions
|
||||||
dump_archive_heap_oopmaps();
|
dump_archive_heap_oopmaps();
|
||||||
|
|
||||||
return buckets_top;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VM_PopulateDumpSharedSpace::doit() {
|
void VM_PopulateDumpSharedSpace::doit() {
|
||||||
@ -1373,14 +1358,11 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
"loader constraints are not saved");
|
"loader constraints are not saved");
|
||||||
guarantee(SystemDictionary::placeholders()->number_of_entries() == 0,
|
guarantee(SystemDictionary::placeholders()->number_of_entries() == 0,
|
||||||
"placeholders are not saved");
|
"placeholders are not saved");
|
||||||
// Revisit and implement this if we prelink method handle call sites:
|
|
||||||
guarantee(SystemDictionary::invoke_method_table() == NULL ||
|
|
||||||
SystemDictionary::invoke_method_table()->number_of_entries() == 0,
|
|
||||||
"invoke method table is not saved");
|
|
||||||
|
|
||||||
// At this point, many classes have been loaded.
|
// At this point, many classes have been loaded.
|
||||||
// Gather systemDictionary classes in a global array and do everything to
|
// Gather systemDictionary classes in a global array and do everything to
|
||||||
// that so we don't have to walk the SystemDictionary again.
|
// that so we don't have to walk the SystemDictionary again.
|
||||||
|
SystemDictionaryShared::check_excluded_classes();
|
||||||
_global_klass_objects = new GrowableArray<Klass*>(1000);
|
_global_klass_objects = new GrowableArray<Klass*>(1000);
|
||||||
CollectClassesClosure collect_classes;
|
CollectClassesClosure collect_classes;
|
||||||
ClassLoaderDataGraph::loaded_classes_do(&collect_classes);
|
ClassLoaderDataGraph::loaded_classes_do(&collect_classes);
|
||||||
@ -1409,21 +1391,11 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
rewrite_nofast_bytecodes_and_calculate_fingerprints();
|
rewrite_nofast_bytecodes_and_calculate_fingerprints();
|
||||||
tty->print_cr("done. ");
|
tty->print_cr("done. ");
|
||||||
|
|
||||||
// Move classes from platform/system dictionaries into the boot dictionary
|
|
||||||
SystemDictionary::combine_shared_dictionaries();
|
|
||||||
|
|
||||||
// Make sure all classes have a correct loader type.
|
|
||||||
ClassLoaderData::the_null_class_loader_data()->dictionary()->classes_do(MetaspaceShared::check_shared_class_loader_type);
|
|
||||||
|
|
||||||
// Remove all references outside the metadata
|
// Remove all references outside the metadata
|
||||||
tty->print("Removing unshareable information ... ");
|
tty->print("Removing unshareable information ... ");
|
||||||
remove_unshareable_in_classes();
|
remove_unshareable_in_classes();
|
||||||
tty->print_cr("done. ");
|
tty->print_cr("done. ");
|
||||||
|
|
||||||
// We don't support archiving unsafe anonymous classes. Verify that they are not stored in
|
|
||||||
// any dictionaries.
|
|
||||||
NOT_PRODUCT(assert_no_unsafe_anonymous_classes_in_dictionaries());
|
|
||||||
|
|
||||||
ArchiveCompactor::initialize();
|
ArchiveCompactor::initialize();
|
||||||
ArchiveCompactor::copy_and_compact();
|
ArchiveCompactor::copy_and_compact();
|
||||||
|
|
||||||
@ -1472,6 +1444,7 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
mapinfo->set_core_spaces_size(core_spaces_size);
|
mapinfo->set_core_spaces_size(core_spaces_size);
|
||||||
|
|
||||||
for (int pass=1; pass<=2; pass++) {
|
for (int pass=1; pass<=2; pass++) {
|
||||||
|
bool print_archive_log = (pass==1);
|
||||||
if (pass == 1) {
|
if (pass == 1) {
|
||||||
// The first pass doesn't actually write the data to disk. All it
|
// The first pass doesn't actually write the data to disk. All it
|
||||||
// does is to update the fields in the mapinfo->_header.
|
// does is to update the fields in the mapinfo->_header.
|
||||||
@ -1496,12 +1469,14 @@ void VM_PopulateDumpSharedSpace::doit() {
|
|||||||
_closed_archive_heap_regions,
|
_closed_archive_heap_regions,
|
||||||
_closed_archive_heap_oopmaps,
|
_closed_archive_heap_oopmaps,
|
||||||
MetaspaceShared::first_closed_archive_heap_region,
|
MetaspaceShared::first_closed_archive_heap_region,
|
||||||
MetaspaceShared::max_closed_archive_heap_region);
|
MetaspaceShared::max_closed_archive_heap_region,
|
||||||
|
print_archive_log);
|
||||||
_total_open_archive_region_size = mapinfo->write_archive_heap_regions(
|
_total_open_archive_region_size = mapinfo->write_archive_heap_regions(
|
||||||
_open_archive_heap_regions,
|
_open_archive_heap_regions,
|
||||||
_open_archive_heap_oopmaps,
|
_open_archive_heap_oopmaps,
|
||||||
MetaspaceShared::first_open_archive_heap_region,
|
MetaspaceShared::first_open_archive_heap_region,
|
||||||
MetaspaceShared::max_open_archive_heap_region);
|
MetaspaceShared::max_open_archive_heap_region,
|
||||||
|
print_archive_log);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapinfo->close();
|
mapinfo->close();
|
||||||
@ -1614,17 +1589,6 @@ class CheckSharedClassesClosure : public KlassClosure {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void MetaspaceShared::check_shared_class_loader_type(InstanceKlass* ik) {
|
|
||||||
ResourceMark rm;
|
|
||||||
if (ik->shared_classpath_index() == UNREGISTERED_INDEX) {
|
|
||||||
guarantee(ik->loader_type() == 0,
|
|
||||||
"Class loader type must not be set for this class %s", ik->name()->as_C_string());
|
|
||||||
} else {
|
|
||||||
guarantee(ik->loader_type() != 0,
|
|
||||||
"Class loader type must be set for this class %s", ik->name()->as_C_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
|
void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
|
||||||
// We need to iterate because verification may cause additional classes
|
// We need to iterate because verification may cause additional classes
|
||||||
// to be loaded.
|
// to be loaded.
|
||||||
@ -1645,9 +1609,6 @@ void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) {
|
|||||||
check_closure.reset();
|
check_closure.reset();
|
||||||
ClassLoaderDataGraph::unlocked_loaded_classes_do(&check_closure);
|
ClassLoaderDataGraph::unlocked_loaded_classes_do(&check_closure);
|
||||||
} while (check_closure.made_progress());
|
} while (check_closure.made_progress());
|
||||||
|
|
||||||
// Unverifiable classes will not be included in the CDS archive.
|
|
||||||
SystemDictionary::remove_classes_in_error_state();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1705,6 +1666,12 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
|||||||
|
|
||||||
log_info(cds)("Shared spaces: preloaded %d classes", class_count);
|
log_info(cds)("Shared spaces: preloaded %d classes", class_count);
|
||||||
|
|
||||||
|
if (SharedArchiveConfigFile) {
|
||||||
|
tty->print_cr("Reading extra data from %s ...", SharedArchiveConfigFile);
|
||||||
|
read_extra_data(SharedArchiveConfigFile, THREAD);
|
||||||
|
}
|
||||||
|
tty->print_cr("Reading extra data: done.");
|
||||||
|
|
||||||
HeapShared::init_subgraph_entry_fields(THREAD);
|
HeapShared::init_subgraph_entry_fields(THREAD);
|
||||||
|
|
||||||
// Rewrite and link classes
|
// Rewrite and link classes
|
||||||
@ -1717,10 +1684,6 @@ void MetaspaceShared::preload_and_dump(TRAPS) {
|
|||||||
link_and_cleanup_shared_classes(CATCH);
|
link_and_cleanup_shared_classes(CATCH);
|
||||||
tty->print_cr("Rewriting and linking classes: done");
|
tty->print_cr("Rewriting and linking classes: done");
|
||||||
|
|
||||||
SystemDictionary::clear_invoke_method_table();
|
|
||||||
|
|
||||||
SystemDictionaryShared::finalize_verification_constraints();
|
|
||||||
|
|
||||||
VM_PopulateDumpSharedSpace op;
|
VM_PopulateDumpSharedSpace op;
|
||||||
VMThread::execute(&op);
|
VMThread::execute(&op);
|
||||||
}
|
}
|
||||||
@ -1732,7 +1695,7 @@ 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()) {
|
||||||
Klass* klass = ClassLoaderExt::load_one_class(&parser, THREAD);
|
Klass* klass = parser.load_current_class(THREAD);
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
if (klass == NULL &&
|
if (klass == NULL &&
|
||||||
(PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) {
|
(PENDING_EXCEPTION->klass()->name() == vmSymbols::java_lang_ClassNotFoundException())) {
|
||||||
@ -1994,21 +1957,6 @@ void MetaspaceShared::initialize_shared_spaces() {
|
|||||||
|
|
||||||
// The rest of the data is now stored in the RW region
|
// The rest of the data is now stored in the RW region
|
||||||
buffer = mapinfo->read_only_tables_start();
|
buffer = mapinfo->read_only_tables_start();
|
||||||
int sharedDictionaryLen = *(intptr_t*)buffer;
|
|
||||||
buffer += sizeof(intptr_t);
|
|
||||||
int number_of_entries = *(intptr_t*)buffer;
|
|
||||||
buffer += sizeof(intptr_t);
|
|
||||||
SystemDictionary::set_shared_dictionary((HashtableBucket<mtClass>*)buffer,
|
|
||||||
sharedDictionaryLen,
|
|
||||||
number_of_entries);
|
|
||||||
buffer += sharedDictionaryLen;
|
|
||||||
|
|
||||||
// The following data are the linked list elements
|
|
||||||
// (HashtableEntry objects) for the shared dictionary table.
|
|
||||||
|
|
||||||
int len = *(intptr_t*)buffer; // skip over shared dictionary entries
|
|
||||||
buffer += sizeof(intptr_t);
|
|
||||||
buffer += len;
|
|
||||||
|
|
||||||
// Verify various attributes of the archive, plus initialize the
|
// Verify various attributes of the archive, plus initialize the
|
||||||
// shared string/symbol tables
|
// shared string/symbol tables
|
||||||
@ -2027,7 +1975,7 @@ void MetaspaceShared::initialize_shared_spaces() {
|
|||||||
if (PrintSharedArchiveAndExit) {
|
if (PrintSharedArchiveAndExit) {
|
||||||
if (PrintSharedDictionary) {
|
if (PrintSharedDictionary) {
|
||||||
tty->print_cr("\nShared classes:\n");
|
tty->print_cr("\nShared classes:\n");
|
||||||
SystemDictionary::print_shared(tty);
|
SystemDictionaryShared::print_on(tty);
|
||||||
}
|
}
|
||||||
if (_archive_loading_failed) {
|
if (_archive_loading_failed) {
|
||||||
tty->print_cr("archive is invalid");
|
tty->print_cr("archive is invalid");
|
||||||
|
@ -107,12 +107,19 @@ class MetaspaceShared : AllStatic {
|
|||||||
static void post_initialize(TRAPS) NOT_CDS_RETURN;
|
static void post_initialize(TRAPS) NOT_CDS_RETURN;
|
||||||
|
|
||||||
// Delta of this object from the bottom of the archive.
|
// Delta of this object from the bottom of the archive.
|
||||||
static uintx object_delta(void* obj) {
|
static uintx object_delta_uintx(void* obj) {
|
||||||
assert(DumpSharedSpaces, "supported only for dumping");
|
assert(DumpSharedSpaces, "supported only for dumping");
|
||||||
assert(shared_rs()->contains(obj), "must be");
|
assert(shared_rs()->contains(obj), "must be");
|
||||||
address base_address = address(shared_rs()->base());
|
address base_address = address(shared_rs()->base());
|
||||||
uintx delta = address(obj) - base_address;
|
uintx deltax = address(obj) - base_address;
|
||||||
return delta;
|
return deltax;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u4 object_delta_u4(void* obj) {
|
||||||
|
// offset is guaranteed to be less than MAX_SHARED_DELTA in DumpRegion::expand_top_to()
|
||||||
|
uintx deltax = object_delta_uintx(obj);
|
||||||
|
guarantee(deltax <= MAX_SHARED_DELTA, "must be 32-bit offset");
|
||||||
|
return (u4)deltax;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_archive_loading_failed() {
|
static void set_archive_loading_failed() {
|
||||||
|
@ -422,7 +422,8 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, Klass
|
|||||||
_static_field_size(parser.static_field_size()),
|
_static_field_size(parser.static_field_size()),
|
||||||
_nonstatic_oop_map_size(nonstatic_oop_map_size(parser.total_oop_map_count())),
|
_nonstatic_oop_map_size(nonstatic_oop_map_size(parser.total_oop_map_count())),
|
||||||
_itable_len(parser.itable_size()),
|
_itable_len(parser.itable_size()),
|
||||||
_reference_type(parser.reference_type()) {
|
_reference_type(parser.reference_type())
|
||||||
|
{
|
||||||
set_vtable_length(parser.vtable_size());
|
set_vtable_length(parser.vtable_size());
|
||||||
set_kind(kind);
|
set_kind(kind);
|
||||||
set_access_flags(parser.access_flags());
|
set_access_flags(parser.access_flags());
|
||||||
@ -433,6 +434,10 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, Klass
|
|||||||
assert(NULL == _methods, "underlying memory not zeroed?");
|
assert(NULL == _methods, "underlying memory not zeroed?");
|
||||||
assert(is_instance_klass(), "is layout incorrect?");
|
assert(is_instance_klass(), "is layout incorrect?");
|
||||||
assert(size_helper() == parser.layout_size(), "incorrect size_helper?");
|
assert(size_helper() == parser.layout_size(), "incorrect size_helper?");
|
||||||
|
|
||||||
|
if (DumpSharedSpaces) {
|
||||||
|
SystemDictionaryShared::init_dumptime_info(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceKlass::deallocate_methods(ClassLoaderData* loader_data,
|
void InstanceKlass::deallocate_methods(ClassLoaderData* loader_data,
|
||||||
@ -579,6 +584,10 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
|||||||
MetadataFactory::free_metadata(loader_data, annotations());
|
MetadataFactory::free_metadata(loader_data, annotations());
|
||||||
}
|
}
|
||||||
set_annotations(NULL);
|
set_annotations(NULL);
|
||||||
|
|
||||||
|
if (DumpSharedSpaces) {
|
||||||
|
SystemDictionaryShared::remove_dumptime_info(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceKlass::should_be_initialized() const {
|
bool InstanceKlass::should_be_initialized() const {
|
||||||
|
@ -176,8 +176,7 @@ private:
|
|||||||
// Flags of the current shared class.
|
// Flags of the current shared class.
|
||||||
u2 _shared_class_flags;
|
u2 _shared_class_flags;
|
||||||
enum {
|
enum {
|
||||||
_has_raw_archived_mirror = 1,
|
_has_raw_archived_mirror = 1
|
||||||
_has_signer_and_not_archived = 1 << 2
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
// The _archived_mirror is set at CDS dump time pointing to the cached mirror
|
// The _archived_mirror is set at CDS dump time pointing to the cached mirror
|
||||||
@ -314,15 +313,6 @@ protected:
|
|||||||
CDS_ONLY(return (_shared_class_flags & _has_raw_archived_mirror) != 0;)
|
CDS_ONLY(return (_shared_class_flags & _has_raw_archived_mirror) != 0;)
|
||||||
NOT_CDS(return false;)
|
NOT_CDS(return false;)
|
||||||
}
|
}
|
||||||
#if INCLUDE_CDS
|
|
||||||
void set_has_signer_and_not_archived() {
|
|
||||||
_shared_class_flags |= _has_signer_and_not_archived;
|
|
||||||
}
|
|
||||||
bool has_signer_and_not_archived() const {
|
|
||||||
assert(DumpSharedSpaces, "dump time only");
|
|
||||||
return (_shared_class_flags & _has_signer_and_not_archived) != 0;
|
|
||||||
}
|
|
||||||
#endif // INCLUDE_CDS
|
|
||||||
|
|
||||||
// Obtain the module or package for this class
|
// Obtain the module or package for this class
|
||||||
virtual ModuleEntry* module() const = 0;
|
virtual ModuleEntry* module() const = 0;
|
||||||
|
@ -942,6 +942,15 @@ private:
|
|||||||
GrowableArray<float> _freqs; // cache frequencies
|
GrowableArray<float> _freqs; // cache frequencies
|
||||||
PhaseIdealLoop* _phase;
|
PhaseIdealLoop* _phase;
|
||||||
|
|
||||||
|
void set_rounding(int mode) {
|
||||||
|
// fesetround is broken on windows
|
||||||
|
NOT_WINDOWS(fesetround(mode);)
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_frequency(float f) {
|
||||||
|
NOT_WINDOWS(assert(f <= 1 && f >= 0, "Incorrect frequency");)
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PathFrequency(Node* dom, PhaseIdealLoop* phase)
|
PathFrequency(Node* dom, PhaseIdealLoop* phase)
|
||||||
: _dom(dom), _stack(0), _phase(phase) {
|
: _dom(dom), _stack(0), _phase(phase) {
|
||||||
@ -949,7 +958,7 @@ public:
|
|||||||
|
|
||||||
float to(Node* n) {
|
float to(Node* n) {
|
||||||
// post order walk on the CFG graph from n to _dom
|
// post order walk on the CFG graph from n to _dom
|
||||||
fesetround(FE_TOWARDZERO); // make sure rounding doesn't push frequency above 1
|
set_rounding(FE_TOWARDZERO); // make sure rounding doesn't push frequency above 1
|
||||||
IdealLoopTree* loop = _phase->get_loop(_dom);
|
IdealLoopTree* loop = _phase->get_loop(_dom);
|
||||||
Node* c = n;
|
Node* c = n;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -976,14 +985,14 @@ public:
|
|||||||
inner_head = inner_loop->_head->as_Loop();
|
inner_head = inner_loop->_head->as_Loop();
|
||||||
inner_head->verify_strip_mined(1);
|
inner_head->verify_strip_mined(1);
|
||||||
}
|
}
|
||||||
fesetround(FE_UPWARD); // make sure rounding doesn't push frequency above 1
|
set_rounding(FE_UPWARD); // make sure rounding doesn't push frequency above 1
|
||||||
float loop_exit_cnt = 0.0f;
|
float loop_exit_cnt = 0.0f;
|
||||||
for (uint i = 0; i < inner_loop->_body.size(); i++) {
|
for (uint i = 0; i < inner_loop->_body.size(); i++) {
|
||||||
Node *n = inner_loop->_body[i];
|
Node *n = inner_loop->_body[i];
|
||||||
float c = inner_loop->compute_profile_trip_cnt_helper(n);
|
float c = inner_loop->compute_profile_trip_cnt_helper(n);
|
||||||
loop_exit_cnt += c;
|
loop_exit_cnt += c;
|
||||||
}
|
}
|
||||||
fesetround(FE_TOWARDZERO);
|
set_rounding(FE_TOWARDZERO);
|
||||||
float cnt = -1;
|
float cnt = -1;
|
||||||
if (n->in(0)->is_If()) {
|
if (n->in(0)->is_If()) {
|
||||||
IfNode* iff = n->in(0)->as_If();
|
IfNode* iff = n->in(0)->as_If();
|
||||||
@ -1003,9 +1012,9 @@ public:
|
|||||||
cnt = p * jmp->_fcnt;
|
cnt = p * jmp->_fcnt;
|
||||||
}
|
}
|
||||||
float this_exit_f = cnt > 0 ? cnt / loop_exit_cnt : 0;
|
float this_exit_f = cnt > 0 ? cnt / loop_exit_cnt : 0;
|
||||||
assert(this_exit_f <= 1 && this_exit_f >= 0, "Incorrect frequency");
|
check_frequency(this_exit_f);
|
||||||
f = f * this_exit_f;
|
f = f * this_exit_f;
|
||||||
assert(f <= 1 && f >= 0, "Incorrect frequency");
|
check_frequency(f);
|
||||||
} else {
|
} else {
|
||||||
float p = -1;
|
float p = -1;
|
||||||
if (n->in(0)->is_If()) {
|
if (n->in(0)->is_If()) {
|
||||||
@ -1018,7 +1027,7 @@ public:
|
|||||||
p = n->in(0)->as_Jump()->_probs[n->as_JumpProj()->_con];
|
p = n->in(0)->as_Jump()->_probs[n->as_JumpProj()->_con];
|
||||||
}
|
}
|
||||||
f = f * p;
|
f = f * p;
|
||||||
assert(f <= 1 && f >= 0, "Incorrect frequency");
|
check_frequency(f);
|
||||||
}
|
}
|
||||||
_freqs.at_put_grow(n->_idx, (float)f, -1);
|
_freqs.at_put_grow(n->_idx, (float)f, -1);
|
||||||
_stack.pop();
|
_stack.pop();
|
||||||
@ -1026,7 +1035,7 @@ public:
|
|||||||
float prev_f = _freqs_stack.pop();
|
float prev_f = _freqs_stack.pop();
|
||||||
float new_f = f;
|
float new_f = f;
|
||||||
f = new_f + prev_f;
|
f = new_f + prev_f;
|
||||||
assert(f <= 1 && f >= 0, "Incorrect frequency");
|
check_frequency(f);
|
||||||
uint i = _stack.index();
|
uint i = _stack.index();
|
||||||
if (i < n->req()) {
|
if (i < n->req()) {
|
||||||
c = n->in(i);
|
c = n->in(i);
|
||||||
@ -1039,8 +1048,8 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_stack.size() == 0) {
|
if (_stack.size() == 0) {
|
||||||
fesetround(FE_TONEAREST);
|
set_rounding(FE_TONEAREST);
|
||||||
assert(f >= 0 && f <= 1, "should have been computed");
|
check_frequency(f);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
} else if (c->is_Loop()) {
|
} else if (c->is_Loop()) {
|
||||||
|
@ -1209,7 +1209,9 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) {
|
|||||||
// Allocate a private array of RegMasks. These RegMasks are not shared.
|
// Allocate a private array of RegMasks. These RegMasks are not shared.
|
||||||
msfpt->_in_rms = NEW_RESOURCE_ARRAY( RegMask, cnt );
|
msfpt->_in_rms = NEW_RESOURCE_ARRAY( RegMask, cnt );
|
||||||
// Empty them all.
|
// Empty them all.
|
||||||
memset( msfpt->_in_rms, 0, sizeof(RegMask)*cnt );
|
for (uint i = 0; i < cnt; i++) {
|
||||||
|
msfpt->_in_rms[i] = RegMask();
|
||||||
|
}
|
||||||
|
|
||||||
// Do all the pre-defined non-Empty register masks
|
// Do all the pre-defined non-Empty register masks
|
||||||
msfpt->_in_rms[TypeFunc::ReturnAdr] = _return_addr_mask;
|
msfpt->_in_rms[TypeFunc::ReturnAdr] = _return_addr_mask;
|
||||||
|
@ -2074,18 +2074,11 @@ bool Arguments::is_bad_option(const JavaVMOption* option, jboolean ignore,
|
|||||||
option_type = ++spacer; // Set both to the empty string.
|
option_type = ++spacer; // Set both to the empty string.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os::obsolete_option(option)) {
|
|
||||||
jio_fprintf(defaultStream::error_stream(),
|
|
||||||
"Obsolete %s%soption: %s\n", option_type, spacer,
|
|
||||||
option->optionString);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
jio_fprintf(defaultStream::error_stream(),
|
jio_fprintf(defaultStream::error_stream(),
|
||||||
"Unrecognized %s%soption: %s\n", option_type, spacer,
|
"Unrecognized %s%soption: %s\n", option_type, spacer,
|
||||||
option->optionString);
|
option->optionString);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static const char* user_assertion_options[] = {
|
static const char* user_assertion_options[] = {
|
||||||
"-da", "-ea", "-disableassertions", "-enableassertions", 0
|
"-da", "-ea", "-disableassertions", "-enableassertions", 0
|
||||||
@ -2649,22 +2642,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
|||||||
// Obsolete in JDK 10
|
// Obsolete in JDK 10
|
||||||
JDK_Version::jdk(10).to_string(version, sizeof(version));
|
JDK_Version::jdk(10).to_string(version, sizeof(version));
|
||||||
warning("Ignoring option %s; support was removed in %s", option->optionString, version);
|
warning("Ignoring option %s; support was removed in %s", option->optionString, version);
|
||||||
// -Xconcurrentio
|
|
||||||
} else if (match_option(option, "-Xconcurrentio")) {
|
|
||||||
if (FLAG_SET_CMDLINE(bool, UseLWPSynchronization, true) != JVMFlag::SUCCESS) {
|
|
||||||
return JNI_EINVAL;
|
|
||||||
}
|
|
||||||
if (FLAG_SET_CMDLINE(bool, BackgroundCompilation, false) != JVMFlag::SUCCESS) {
|
|
||||||
return JNI_EINVAL;
|
|
||||||
}
|
|
||||||
SafepointSynchronize::set_defer_thr_suspend_loop_count();
|
|
||||||
if (FLAG_SET_CMDLINE(bool, UseTLAB, false) != JVMFlag::SUCCESS) {
|
|
||||||
return JNI_EINVAL;
|
|
||||||
}
|
|
||||||
if (FLAG_SET_CMDLINE(size_t, NewSizeThreadIncrease, 16 * K) != JVMFlag::SUCCESS) { // 20Kb per thread added to new generation
|
|
||||||
return JNI_EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -Xinternalversion
|
// -Xinternalversion
|
||||||
} else if (match_option(option, "-Xinternalversion")) {
|
} else if (match_option(option, "-Xinternalversion")) {
|
||||||
jio_fprintf(defaultStream::output_stream(), "%s\n",
|
jio_fprintf(defaultStream::output_stream(), "%s\n",
|
||||||
|
@ -802,9 +802,6 @@ class os: AllStatic {
|
|||||||
// System loadavg support. Returns -1 if load average cannot be obtained.
|
// System loadavg support. Returns -1 if load average cannot be obtained.
|
||||||
static int loadavg(double loadavg[], int nelem);
|
static int loadavg(double loadavg[], int nelem);
|
||||||
|
|
||||||
// Hook for os specific jvm options that we don't want to abort on seeing
|
|
||||||
static bool obsolete_option(const JavaVMOption *option);
|
|
||||||
|
|
||||||
// Amount beyond the callee frame size that we bang the stack.
|
// Amount beyond the callee frame size that we bang the stack.
|
||||||
static int extra_bang_size_in_bytes();
|
static int extra_bang_size_in_bytes();
|
||||||
|
|
||||||
|
@ -178,10 +178,6 @@ public:
|
|||||||
|
|
||||||
static address safepoint_counter_addr() { return (address)&_safepoint_counter; }
|
static address safepoint_counter_addr() { return (address)&_safepoint_counter; }
|
||||||
|
|
||||||
// This method is only used for -Xconcurrentio support.
|
|
||||||
static void set_defer_thr_suspend_loop_count() {
|
|
||||||
_defer_thr_suspend_loop_count = 1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Some helper assert macros for safepoint checks.
|
// Some helper assert macros for safepoint checks.
|
||||||
|
@ -404,6 +404,8 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
|||||||
/* Memory */ \
|
/* Memory */ \
|
||||||
/**********/ \
|
/**********/ \
|
||||||
\
|
\
|
||||||
|
static_field(MetaspaceObj, _shared_metaspace_base, void*) \
|
||||||
|
static_field(MetaspaceObj, _shared_metaspace_top, void*) \
|
||||||
nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \
|
nonstatic_field(ThreadLocalAllocBuffer, _start, HeapWord*) \
|
||||||
nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \
|
nonstatic_field(ThreadLocalAllocBuffer, _top, HeapWord*) \
|
||||||
nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \
|
nonstatic_field(ThreadLocalAllocBuffer, _end, HeapWord*) \
|
||||||
@ -460,7 +462,6 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
|||||||
/* SystemDictionary */ \
|
/* SystemDictionary */ \
|
||||||
/********************/ \
|
/********************/ \
|
||||||
\
|
\
|
||||||
static_field(SystemDictionary, _shared_dictionary, Dictionary*) \
|
|
||||||
static_field(SystemDictionary, _system_loader_lock_obj, oop) \
|
static_field(SystemDictionary, _system_loader_lock_obj, oop) \
|
||||||
static_field(SystemDictionary, WK_KLASS(Object_klass), InstanceKlass*) \
|
static_field(SystemDictionary, WK_KLASS(Object_klass), InstanceKlass*) \
|
||||||
static_field(SystemDictionary, WK_KLASS(String_klass), InstanceKlass*) \
|
static_field(SystemDictionary, WK_KLASS(String_klass), InstanceKlass*) \
|
||||||
|
@ -472,6 +472,12 @@ class ConcurrentHashTable : public CHeapObj<F> {
|
|||||||
template <typename SCAN_FUNC>
|
template <typename SCAN_FUNC>
|
||||||
void do_scan(Thread* thread, SCAN_FUNC& scan_f);
|
void do_scan(Thread* thread, SCAN_FUNC& scan_f);
|
||||||
|
|
||||||
|
// Visit all items with SCAN_FUNC without any protection.
|
||||||
|
// It will assume there is no other thread accessing this
|
||||||
|
// table during the safepoint. Must be called with VM thread.
|
||||||
|
template <typename SCAN_FUNC>
|
||||||
|
void do_safepoint_scan(SCAN_FUNC& scan_f);
|
||||||
|
|
||||||
// Destroying items matching EVALUATE_FUNC, before destroying items
|
// Destroying items matching EVALUATE_FUNC, before destroying items
|
||||||
// DELETE_FUNC is called, if resize lock is obtained. Else returns false.
|
// DELETE_FUNC is called, if resize lock is obtained. Else returns false.
|
||||||
template <typename EVALUATE_FUNC, typename DELETE_FUNC>
|
template <typename EVALUATE_FUNC, typename DELETE_FUNC>
|
||||||
|
@ -1116,6 +1116,8 @@ template <typename SCAN_FUNC>
|
|||||||
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
||||||
do_scan(Thread* thread, SCAN_FUNC& scan_f)
|
do_scan(Thread* thread, SCAN_FUNC& scan_f)
|
||||||
{
|
{
|
||||||
|
assert(!SafepointSynchronize::is_at_safepoint(),
|
||||||
|
"must be outside a safepoint");
|
||||||
assert(_resize_lock_owner != thread, "Re-size lock held");
|
assert(_resize_lock_owner != thread, "Re-size lock held");
|
||||||
lock_resize_lock(thread);
|
lock_resize_lock(thread);
|
||||||
do_scan_locked(thread, scan_f);
|
do_scan_locked(thread, scan_f);
|
||||||
@ -1123,6 +1125,49 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
|||||||
assert(_resize_lock_owner != thread, "Re-size lock held");
|
assert(_resize_lock_owner != thread, "Re-size lock held");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename VALUE, typename CONFIG, MEMFLAGS F>
|
||||||
|
template <typename SCAN_FUNC>
|
||||||
|
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
||||||
|
do_safepoint_scan(SCAN_FUNC& scan_f)
|
||||||
|
{
|
||||||
|
// We only allow this method to be used during a safepoint.
|
||||||
|
assert(SafepointSynchronize::is_at_safepoint(),
|
||||||
|
"must only be called in a safepoint");
|
||||||
|
assert(Thread::current()->is_VM_thread(),
|
||||||
|
"should be in vm thread");
|
||||||
|
|
||||||
|
// Here we skip protection,
|
||||||
|
// thus no other thread may use this table at the same time.
|
||||||
|
InternalTable* table = get_table();
|
||||||
|
for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) {
|
||||||
|
Bucket* bucket = table->get_bucket(bucket_it);
|
||||||
|
// If bucket have a redirect the items will be in the new table.
|
||||||
|
// We must visit them there since the new table will contain any
|
||||||
|
// concurrent inserts done after this bucket was resized.
|
||||||
|
// If the bucket don't have redirect flag all items is in this table.
|
||||||
|
if (!bucket->have_redirect()) {
|
||||||
|
if(!visit_nodes(bucket, scan_f)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(bucket->is_locked(), "Bucket must be locked.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If there is a paused resize we also need to visit the already resized items.
|
||||||
|
table = get_new_table();
|
||||||
|
if (table == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DEBUG_ONLY(if (table == POISON_PTR) { return; })
|
||||||
|
for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) {
|
||||||
|
Bucket* bucket = table->get_bucket(bucket_it);
|
||||||
|
assert(!bucket->is_locked(), "Bucket must be unlocked.");
|
||||||
|
if (!visit_nodes(bucket, scan_f)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename VALUE, typename CONFIG, MEMFLAGS F>
|
template <typename VALUE, typename CONFIG, MEMFLAGS F>
|
||||||
template <typename EVALUATE_FUNC, typename DELETE_FUNC>
|
template <typename EVALUATE_FUNC, typename DELETE_FUNC>
|
||||||
inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
|
inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
|
||||||
@ -1142,6 +1187,8 @@ template <typename EVALUATE_FUNC, typename DELETE_FUNC>
|
|||||||
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
|
||||||
bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f)
|
bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f)
|
||||||
{
|
{
|
||||||
|
assert(!SafepointSynchronize::is_at_safepoint(),
|
||||||
|
"must be outside a safepoint");
|
||||||
lock_resize_lock(thread);
|
lock_resize_lock(thread);
|
||||||
do_bulk_delete_locked(thread, eval_f, del_f);
|
do_bulk_delete_locked(thread, eval_f, del_f);
|
||||||
unlock_resize_lock(thread);
|
unlock_resize_lock(thread);
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
#include "classfile/stringTable.hpp"
|
#include "classfile/stringTable.hpp"
|
||||||
#include "logging/log.hpp"
|
#include "logging/log.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "memory/metaspaceShared.hpp"
|
|
||||||
#include "memory/resourceArea.hpp"
|
#include "memory/resourceArea.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
#include "oops/weakHandle.inline.hpp"
|
#include "oops/weakHandle.inline.hpp"
|
||||||
@ -99,11 +98,7 @@ template <class T, MEMFLAGS F> HashtableEntry<T, F>* Hashtable<T, F>::allocate_n
|
|||||||
|
|
||||||
template <MEMFLAGS F> void BasicHashtable<F>::free_buckets() {
|
template <MEMFLAGS F> void BasicHashtable<F>::free_buckets() {
|
||||||
if (NULL != _buckets) {
|
if (NULL != _buckets) {
|
||||||
// Don't delete the buckets in the shared space. They aren't
|
|
||||||
// allocated by os::malloc
|
|
||||||
if (!MetaspaceShared::is_in_shared_metaspace(_buckets)) {
|
|
||||||
FREE_C_HEAP_ARRAY(HashtableBucket, _buckets);
|
FREE_C_HEAP_ARRAY(HashtableBucket, _buckets);
|
||||||
}
|
|
||||||
_buckets = NULL;
|
_buckets = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,47 +132,6 @@ template <MEMFLAGS F> void BasicHashtable<F>::bulk_free_entries(BucketUnlinkCont
|
|||||||
}
|
}
|
||||||
Atomic::add(-context->_num_removed, &_number_of_entries);
|
Atomic::add(-context->_num_removed, &_number_of_entries);
|
||||||
}
|
}
|
||||||
// Copy the table to the shared space.
|
|
||||||
template <MEMFLAGS F> size_t BasicHashtable<F>::count_bytes_for_table() {
|
|
||||||
size_t bytes = 0;
|
|
||||||
bytes += sizeof(intptr_t); // len
|
|
||||||
|
|
||||||
for (int i = 0; i < _table_size; ++i) {
|
|
||||||
for (BasicHashtableEntry<F>** p = _buckets[i].entry_addr();
|
|
||||||
*p != NULL;
|
|
||||||
p = (*p)->next_addr()) {
|
|
||||||
bytes += entry_size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the hash table entries (into CDS archive)
|
|
||||||
template <MEMFLAGS F> void BasicHashtable<F>::copy_table(char* top, char* end) {
|
|
||||||
assert(is_aligned(top, sizeof(intptr_t)), "bad alignment");
|
|
||||||
intptr_t *plen = (intptr_t*)(top);
|
|
||||||
top += sizeof(*plen);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < _table_size; ++i) {
|
|
||||||
for (BasicHashtableEntry<F>** p = _buckets[i].entry_addr();
|
|
||||||
*p != NULL;
|
|
||||||
p = (*p)->next_addr()) {
|
|
||||||
*p = (BasicHashtableEntry<F>*)memcpy(top, (void*)*p, entry_size());
|
|
||||||
top += entry_size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*plen = (char*)(top) - (char*)plen - sizeof(*plen);
|
|
||||||
assert(top == end, "count_bytes_for_table is wrong");
|
|
||||||
// Set the shared bit.
|
|
||||||
|
|
||||||
for (i = 0; i < _table_size; ++i) {
|
|
||||||
for (BasicHashtableEntry<F>* p = bucket(i); p != NULL; p = p->next()) {
|
|
||||||
p->set_shared();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For oops and Strings the size of the literal is interesting. For other types, nobody cares.
|
// For oops and Strings the size of the literal is interesting. For other types, nobody cares.
|
||||||
static int literal_size(ConstantPool*) { return 0; }
|
static int literal_size(ConstantPool*) { return 0; }
|
||||||
@ -297,34 +251,6 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outp
|
|||||||
st->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
|
st->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Dump the hash table buckets.
|
|
||||||
|
|
||||||
template <MEMFLAGS F> size_t BasicHashtable<F>::count_bytes_for_buckets() {
|
|
||||||
size_t bytes = 0;
|
|
||||||
bytes += sizeof(intptr_t); // len
|
|
||||||
bytes += sizeof(intptr_t); // _number_of_entries
|
|
||||||
bytes += _table_size * sizeof(HashtableBucket<F>); // the buckets
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the buckets (into CDS archive)
|
|
||||||
template <MEMFLAGS F> void BasicHashtable<F>::copy_buckets(char* top, char* end) {
|
|
||||||
assert(is_aligned(top, sizeof(intptr_t)), "bad alignment");
|
|
||||||
intptr_t len = _table_size * sizeof(HashtableBucket<F>);
|
|
||||||
*(intptr_t*)(top) = len;
|
|
||||||
top += sizeof(intptr_t);
|
|
||||||
|
|
||||||
*(intptr_t*)(top) = _number_of_entries;
|
|
||||||
top += sizeof(intptr_t);
|
|
||||||
|
|
||||||
_buckets = (HashtableBucket<F>*)memcpy(top, (void*)_buckets, len);
|
|
||||||
top += len;
|
|
||||||
|
|
||||||
assert(top == end, "count_bytes_for_buckets is wrong");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
template <class T> void print_literal(T l) {
|
template <class T> void print_literal(T l) {
|
||||||
l->print();
|
l->print();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -147,12 +147,6 @@ public:
|
|||||||
BasicHashtable(int table_size, int entry_size,
|
BasicHashtable(int table_size, int entry_size,
|
||||||
HashtableBucket<F>* buckets, int number_of_entries);
|
HashtableBucket<F>* buckets, int number_of_entries);
|
||||||
|
|
||||||
// Sharing support.
|
|
||||||
size_t count_bytes_for_buckets();
|
|
||||||
size_t count_bytes_for_table();
|
|
||||||
void copy_buckets(char* top, char* end);
|
|
||||||
void copy_table(char* top, char* end);
|
|
||||||
|
|
||||||
// Bucket handling
|
// Bucket handling
|
||||||
int hash_to_index(unsigned int full_hash) const {
|
int hash_to_index(unsigned int full_hash) const {
|
||||||
int h = full_hash % _table_size;
|
int h = full_hash % _table_size;
|
||||||
|
@ -1433,7 +1433,7 @@ public final class Module implements AnnotatedElement {
|
|||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||||
+ ClassWriter.COMPUTE_FRAMES);
|
+ ClassWriter.COMPUTE_FRAMES);
|
||||||
|
|
||||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
|
||||||
@Override
|
@Override
|
||||||
public void visit(int version,
|
public void visit(int version,
|
||||||
int access,
|
int access,
|
||||||
|
@ -1629,7 +1629,7 @@ public final class StringConcatFactory {
|
|||||||
@ForceInline
|
@ForceInline
|
||||||
private static byte[] newArray(long indexCoder) {
|
private static byte[] newArray(long indexCoder) {
|
||||||
byte coder = (byte)(indexCoder >> 32);
|
byte coder = (byte)(indexCoder >> 32);
|
||||||
int index = ((int)indexCoder & 0x7FFFFFFF);
|
int index = (int)indexCoder;
|
||||||
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
|
return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1692,30 +1692,35 @@ public final class StringConcatFactory {
|
|||||||
// no instantiation
|
// no instantiation
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StringifierMost extends ClassValue<MethodHandle> {
|
private static class ObjectStringifier {
|
||||||
@Override
|
|
||||||
protected MethodHandle computeValue(Class<?> cl) {
|
|
||||||
if (cl == String.class) {
|
|
||||||
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
|
|
||||||
} else if (cl == float.class) {
|
|
||||||
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
|
|
||||||
} else if (cl == double.class) {
|
|
||||||
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
|
|
||||||
} else if (!cl.isPrimitive()) {
|
|
||||||
MethodHandle mhObject = lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, Object.class);
|
|
||||||
|
|
||||||
// We need the additional conversion here, because String.valueOf(Object) may return null.
|
// We need some additional conversion for Objects in general, because String.valueOf(Object)
|
||||||
// String conversion rules in Java state we need to produce "null" String in this case.
|
// may return null. String conversion rules in Java state we need to produce "null" String
|
||||||
// It can be easily done with applying valueOf the second time.
|
// in this case, so we provide a customized version that deals with this problematic corner case.
|
||||||
return MethodHandles.filterReturnValue(mhObject,
|
private static String valueOf(Object value) {
|
||||||
mhObject.asType(MethodType.methodType(String.class, String.class)));
|
String s;
|
||||||
|
return (value == null || (s = value.toString()) == null) ? "null" : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// Could have used MethodHandles.lookup() instead of Lookup.IMPL_LOOKUP, if not for the fact
|
||||||
|
// java.lang.invoke Lookups are explicitly forbidden to be retrieved using that API.
|
||||||
|
private static final MethodHandle INSTANCE =
|
||||||
|
lookupStatic(Lookup.IMPL_LOOKUP, ObjectStringifier.class, "valueOf", String.class, Object.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class FloatStringifiers {
|
||||||
|
private static final MethodHandle FLOAT_INSTANCE =
|
||||||
|
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, float.class);
|
||||||
|
|
||||||
|
private static final MethodHandle DOUBLE_INSTANCE =
|
||||||
|
lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, double.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StringifierAny extends ClassValue<MethodHandle> {
|
private static class StringifierAny extends ClassValue<MethodHandle> {
|
||||||
|
|
||||||
|
private static final ClassValue<MethodHandle> INSTANCE = new StringifierAny();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected MethodHandle computeValue(Class<?> cl) {
|
protected MethodHandle computeValue(Class<?> cl) {
|
||||||
if (cl == byte.class || cl == short.class || cl == int.class) {
|
if (cl == byte.class || cl == short.class || cl == int.class) {
|
||||||
@ -1727,7 +1732,7 @@ public final class StringConcatFactory {
|
|||||||
} else if (cl == long.class) {
|
} else if (cl == long.class) {
|
||||||
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
|
return lookupStatic(MethodHandles.publicLookup(), String.class, "valueOf", String.class, long.class);
|
||||||
} else {
|
} else {
|
||||||
MethodHandle mh = STRINGIFIERS_MOST.get(cl);
|
MethodHandle mh = forMost(cl);
|
||||||
if (mh != null) {
|
if (mh != null) {
|
||||||
return mh;
|
return mh;
|
||||||
} else {
|
} else {
|
||||||
@ -1737,9 +1742,6 @@ public final class StringConcatFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ClassValue<MethodHandle> STRINGIFIERS_MOST = new StringifierMost();
|
|
||||||
private static final ClassValue<MethodHandle> STRINGIFIERS_ANY = new StringifierAny();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a stringifier for references and floats/doubles only.
|
* Returns a stringifier for references and floats/doubles only.
|
||||||
* Always returns null for other primitives.
|
* Always returns null for other primitives.
|
||||||
@ -1748,7 +1750,14 @@ public final class StringConcatFactory {
|
|||||||
* @return stringifier; null, if not available
|
* @return stringifier; null, if not available
|
||||||
*/
|
*/
|
||||||
static MethodHandle forMost(Class<?> t) {
|
static MethodHandle forMost(Class<?> t) {
|
||||||
return STRINGIFIERS_MOST.get(t);
|
if (!t.isPrimitive()) {
|
||||||
|
return ObjectStringifier.INSTANCE;
|
||||||
|
} else if (t == float.class) {
|
||||||
|
return FloatStringifiers.FLOAT_INSTANCE;
|
||||||
|
} else if (t == double.class) {
|
||||||
|
return FloatStringifiers.DOUBLE_INSTANCE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1758,7 +1767,7 @@ public final class StringConcatFactory {
|
|||||||
* @return stringifier
|
* @return stringifier
|
||||||
*/
|
*/
|
||||||
static MethodHandle forAny(Class<?> t) {
|
static MethodHandle forAny(Class<?> t) {
|
||||||
return STRINGIFIERS_ANY.get(t);
|
return StringifierAny.INSTANCE.get(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,7 +35,7 @@ import static sun.invoke.util.Wrapper.*;
|
|||||||
class TypeConvertingMethodAdapter extends MethodVisitor {
|
class TypeConvertingMethodAdapter extends MethodVisitor {
|
||||||
|
|
||||||
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
TypeConvertingMethodAdapter(MethodVisitor mv) {
|
||||||
super(Opcodes.ASM5, mv);
|
super(Opcodes.ASM7, mv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int NUM_WRAPPERS = Wrapper.COUNT;
|
private static final int NUM_WRAPPERS = Wrapper.COUNT;
|
||||||
|
@ -379,7 +379,7 @@ public abstract class Reference<T> {
|
|||||||
* Throws {@link CloneNotSupportedException}. A {@code Reference} cannot be
|
* Throws {@link CloneNotSupportedException}. A {@code Reference} cannot be
|
||||||
* meaningfully cloned. Construct a new {@code Reference} instead.
|
* meaningfully cloned. Construct a new {@code Reference} instead.
|
||||||
*
|
*
|
||||||
* @returns never returns normally
|
* @return never returns normally
|
||||||
* @throws CloneNotSupportedException always
|
* @throws CloneNotSupportedException always
|
||||||
*
|
*
|
||||||
* @since 11
|
* @since 11
|
||||||
|
@ -62,7 +62,7 @@ import sun.util.logging.PlatformLogger;
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>
|
* <li>
|
||||||
* CookieHandler is at the core of cookie management. User can call
|
* CookieHandler is at the core of cookie management. User can call
|
||||||
* CookieHandler.setDefault to set a concrete CookieHanlder implementation
|
* CookieHandler.setDefault to set a concrete CookieHandler implementation
|
||||||
* to be used.
|
* to be used.
|
||||||
* </li>
|
* </li>
|
||||||
* <li>
|
* <li>
|
||||||
@ -354,7 +354,7 @@ public class CookieManager extends CookieHandler
|
|||||||
private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
|
private boolean shouldAcceptInternal(URI uri, HttpCookie cookie) {
|
||||||
try {
|
try {
|
||||||
return policyCallback.shouldAccept(uri, cookie);
|
return policyCallback.shouldAccept(uri, cookie);
|
||||||
} catch (Exception ignored) { // pretect against malicious callback
|
} catch (Exception ignored) { // protect against malicious callback
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,7 @@ class DatagramSocket implements java.io.Closeable {
|
|||||||
private void checkOldImpl() {
|
private void checkOldImpl() {
|
||||||
if (impl == null)
|
if (impl == null)
|
||||||
return;
|
return;
|
||||||
// DatagramSocketImpl.peekdata() is a protected method, therefore we need to use
|
// DatagramSocketImpl.peekData() is a protected method, therefore we need to use
|
||||||
// getDeclaredMethod, therefore we need permission to access the member
|
// getDeclaredMethod, therefore we need permission to access the member
|
||||||
try {
|
try {
|
||||||
AccessController.doPrivileged(
|
AccessController.doPrivileged(
|
||||||
@ -660,7 +660,7 @@ class DatagramSocket implements java.io.Closeable {
|
|||||||
throw new SocketException("Socket is closed");
|
throw new SocketException("Socket is closed");
|
||||||
checkAddress (p.getAddress(), "send");
|
checkAddress (p.getAddress(), "send");
|
||||||
if (connectState == ST_NOT_CONNECTED) {
|
if (connectState == ST_NOT_CONNECTED) {
|
||||||
// check the address is ok wiht the security manager on every send.
|
// check the address is ok with the security manager on every send.
|
||||||
SecurityManager security = System.getSecurityManager();
|
SecurityManager security = System.getSecurityManager();
|
||||||
|
|
||||||
// The reason you want to synchronize on datagram packet
|
// The reason you want to synchronize on datagram packet
|
||||||
@ -1070,7 +1070,7 @@ class DatagramSocket implements java.io.Closeable {
|
|||||||
*
|
*
|
||||||
* @param on whether to enable or disable the
|
* @param on whether to enable or disable the
|
||||||
* @exception SocketException if an error occurs enabling or
|
* @exception SocketException if an error occurs enabling or
|
||||||
* disabling the {@code SO_RESUEADDR} socket option,
|
* disabling the {@code SO_REUSEADDR} socket option,
|
||||||
* or the socket is closed.
|
* or the socket is closed.
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
* @see #getReuseAddress()
|
* @see #getReuseAddress()
|
||||||
|
@ -666,7 +666,7 @@ public final class HttpCookie implements Cloneable {
|
|||||||
int domainLength = domain.length();
|
int domainLength = domain.length();
|
||||||
int lengthDiff = host.length() - domainLength;
|
int lengthDiff = host.length() - domainLength;
|
||||||
if (lengthDiff == 0) {
|
if (lengthDiff == 0) {
|
||||||
// if the host name and the domain name are just string-compare euqal
|
// if the host name and the domain name are just string-compare equal
|
||||||
return host.equalsIgnoreCase(domain);
|
return host.equalsIgnoreCase(domain);
|
||||||
}
|
}
|
||||||
else if (lengthDiff > 0) {
|
else if (lengthDiff > 0) {
|
||||||
@ -1131,7 +1131,7 @@ public final class HttpCookie implements Cloneable {
|
|||||||
* Split cookie header string according to rfc 2965:
|
* Split cookie header string according to rfc 2965:
|
||||||
* 1) split where it is a comma;
|
* 1) split where it is a comma;
|
||||||
* 2) but not the comma surrounding by double-quotes, which is the comma
|
* 2) but not the comma surrounding by double-quotes, which is the comma
|
||||||
* inside port list or embeded URIs.
|
* inside port list or embedded URIs.
|
||||||
*
|
*
|
||||||
* @param header
|
* @param header
|
||||||
* the cookie header string to split
|
* the cookie header string to split
|
||||||
|
@ -407,7 +407,7 @@ public final class IDN {
|
|||||||
// 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen
|
// 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen
|
||||||
// <->.
|
// <->.
|
||||||
// Non LDH refers to characters in the ASCII range, but which are not
|
// Non LDH refers to characters in the ASCII range, but which are not
|
||||||
// letters, digits or the hypen.
|
// letters, digits or the hyphen.
|
||||||
//
|
//
|
||||||
// non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F
|
// non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F
|
||||||
//
|
//
|
||||||
|
@ -54,7 +54,7 @@ class InMemoryCookieStore implements CookieStore {
|
|||||||
private Map<String, List<HttpCookie>> domainIndex = null;
|
private Map<String, List<HttpCookie>> domainIndex = null;
|
||||||
private Map<URI, List<HttpCookie>> uriIndex = null;
|
private Map<URI, List<HttpCookie>> uriIndex = null;
|
||||||
|
|
||||||
// use ReentrantLock instead of syncronized for scalability
|
// use ReentrantLock instead of synchronized for scalability
|
||||||
private ReentrantLock lock = null;
|
private ReentrantLock lock = null;
|
||||||
|
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ class InMemoryCookieStore implements CookieStore {
|
|||||||
int domainLength = domain.length();
|
int domainLength = domain.length();
|
||||||
int lengthDiff = host.length() - domainLength;
|
int lengthDiff = host.length() - domainLength;
|
||||||
if (lengthDiff == 0) {
|
if (lengthDiff == 0) {
|
||||||
// if the host name and the domain name are just string-compare euqal
|
// if the host name and the domain name are just string-compare equal
|
||||||
return host.equalsIgnoreCase(domain);
|
return host.equalsIgnoreCase(domain);
|
||||||
} else if (lengthDiff > 0) {
|
} else if (lengthDiff > 0) {
|
||||||
// need to check H & D component
|
// need to check H & D component
|
||||||
@ -301,7 +301,7 @@ class InMemoryCookieStore implements CookieStore {
|
|||||||
toRemove.add(c);
|
toRemove.add(c);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the cookie has beed removed from main store,
|
// the cookie has been removed from main store,
|
||||||
// so also remove it from domain indexed store
|
// so also remove it from domain indexed store
|
||||||
toRemove.add(c);
|
toRemove.add(c);
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ class InMemoryCookieStore implements CookieStore {
|
|||||||
cookieJar.remove(ck);
|
cookieJar.remove(ck);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// the cookie has beed removed from main store,
|
// the cookie has been removed from main store,
|
||||||
// so also remove it from domain indexed store
|
// so also remove it from domain indexed store
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ class Inet4Address extends InetAddress {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility routine to check if the InetAddress is a wildcard address.
|
* Utility routine to check if the InetAddress is a wildcard address.
|
||||||
* @return a {@code boolean} indicating if the Inetaddress is
|
* @return a {@code boolean} indicating if the InetAddress is
|
||||||
* a wildcard address.
|
* a wildcard address.
|
||||||
*/
|
*/
|
||||||
public boolean isAnyLocalAddress() {
|
public boolean isAnyLocalAddress() {
|
||||||
|
@ -415,7 +415,7 @@ class Inet6Address extends InetAddress {
|
|||||||
* set to the value corresponding to the given interface for the address
|
* set to the value corresponding to the given interface for the address
|
||||||
* type specified in {@code addr}. The call will fail with an
|
* type specified in {@code addr}. The call will fail with an
|
||||||
* UnknownHostException if the given interface does not have a numeric
|
* UnknownHostException if the given interface does not have a numeric
|
||||||
* scope_id assigned for the given address type (eg. link-local or site-local).
|
* scope_id assigned for the given address type (e.g. link-local or site-local).
|
||||||
* See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6
|
* See <a href="Inet6Address.html#scoped">here</a> for a description of IPv6
|
||||||
* scoped addresses.
|
* scoped addresses.
|
||||||
*
|
*
|
||||||
@ -507,7 +507,7 @@ class Inet6Address extends InetAddress {
|
|||||||
|
|
||||||
/* check the two Ipv6 addresses and return false if they are both
|
/* check the two Ipv6 addresses and return false if they are both
|
||||||
* non global address types, but not the same.
|
* non global address types, but not the same.
|
||||||
* (ie. one is sitelocal and the other linklocal)
|
* (i.e. one is site-local and the other link-local)
|
||||||
* return true otherwise.
|
* return true otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -683,7 +683,7 @@ class Inet6Address extends InetAddress {
|
|||||||
/**
|
/**
|
||||||
* Utility routine to check if the InetAddress is a wildcard address.
|
* Utility routine to check if the InetAddress is a wildcard address.
|
||||||
*
|
*
|
||||||
* @return a {@code boolean} indicating if the Inetaddress is
|
* @return a {@code boolean} indicating if the InetAddress is
|
||||||
* a wildcard address.
|
* a wildcard address.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -821,7 +821,7 @@ class Inet6Address extends InetAddress {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the scoped interface, if this instance was created with
|
* Returns the scoped interface, if this instance was created with
|
||||||
* with a scoped interface.
|
* a scoped interface.
|
||||||
*
|
*
|
||||||
* @return the scoped interface, or null if not set.
|
* @return the scoped interface, or null if not set.
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
|
@ -377,7 +377,7 @@ class InetAddress implements java.io.Serializable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility routine to check if the InetAddress is a wildcard address.
|
* Utility routine to check if the InetAddress is a wildcard address.
|
||||||
* @return a {@code boolean} indicating if the Inetaddress is
|
* @return a {@code boolean} indicating if the InetAddress is
|
||||||
* a wildcard address.
|
* a wildcard address.
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
@ -1022,7 +1022,7 @@ class InetAddress implements java.io.Serializable {
|
|||||||
* <p>Lookup a host mapping by name. Retrieve the IP addresses
|
* <p>Lookup a host mapping by name. Retrieve the IP addresses
|
||||||
* associated with a host.
|
* associated with a host.
|
||||||
*
|
*
|
||||||
* <p>Search the configured hosts file for the addresses assocaited with
|
* <p>Search the configured hosts file for the addresses associated
|
||||||
* with the specified host name.
|
* with the specified host name.
|
||||||
*
|
*
|
||||||
* @param host the specified hostname
|
* @param host the specified hostname
|
||||||
@ -1038,7 +1038,7 @@ class InetAddress implements java.io.Serializable {
|
|||||||
byte addr[] = new byte[4];
|
byte addr[] = new byte[4];
|
||||||
ArrayList<InetAddress> inetAddresses = null;
|
ArrayList<InetAddress> inetAddresses = null;
|
||||||
|
|
||||||
// lookup the file and create a list InetAddress for the specfied host
|
// lookup the file and create a list InetAddress for the specified host
|
||||||
try (Scanner hostsFileScanner = new Scanner(new File(hostsFile), "UTF-8")) {
|
try (Scanner hostsFileScanner = new Scanner(new File(hostsFile), "UTF-8")) {
|
||||||
while (hostsFileScanner.hasNextLine()) {
|
while (hostsFileScanner.hasNextLine()) {
|
||||||
hostEntry = hostsFileScanner.nextLine();
|
hostEntry = hostsFileScanner.nextLine();
|
||||||
@ -1341,7 +1341,7 @@ class InetAddress implements java.io.Serializable {
|
|||||||
throw new UnknownHostException(host + ": invalid IPv6 address");
|
throw new UnknownHostException(host + ": invalid IPv6 address");
|
||||||
}
|
}
|
||||||
} else if (ipv6Expected) {
|
} else if (ipv6Expected) {
|
||||||
// Means an IPv4 litteral between brackets!
|
// Means an IPv4 literal between brackets!
|
||||||
throw new UnknownHostException("["+host+"]");
|
throw new UnknownHostException("["+host+"]");
|
||||||
}
|
}
|
||||||
InetAddress[] ret = new InetAddress[1];
|
InetAddress[] ret = new InetAddress[1];
|
||||||
@ -1358,7 +1358,7 @@ class InetAddress implements java.io.Serializable {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else if (ipv6Expected) {
|
} else if (ipv6Expected) {
|
||||||
// We were expecting an IPv6 Litteral, but got something else
|
// We were expecting an IPv6 Literal, but got something else
|
||||||
throw new UnknownHostException("["+host+"]");
|
throw new UnknownHostException("["+host+"]");
|
||||||
}
|
}
|
||||||
return getAllByName0(host, reqAddr, true, true);
|
return getAllByName0(host, reqAddr, true, true);
|
||||||
|
@ -170,7 +170,7 @@ public final class NetworkInterface {
|
|||||||
* a SecurityException will be returned in the List.
|
* a SecurityException will be returned in the List.
|
||||||
*
|
*
|
||||||
* @return a {@code List} object with all or a subset of the
|
* @return a {@code List} object with all or a subset of the
|
||||||
* InterfaceAddresss of this network interface
|
* InterfaceAddress of this network interface
|
||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public java.util.List<InterfaceAddress> getInterfaceAddresses() {
|
public java.util.List<InterfaceAddress> getInterfaceAddresses() {
|
||||||
|
@ -136,7 +136,7 @@ public abstract class ProxySelector {
|
|||||||
* @param uri
|
* @param uri
|
||||||
* The URI that a connection is required to
|
* The URI that a connection is required to
|
||||||
*
|
*
|
||||||
* @return a List of Proxies. Each element in the
|
* @return a List of Proxies. Each element in
|
||||||
* the List is of type
|
* the List is of type
|
||||||
* {@link java.net.Proxy Proxy};
|
* {@link java.net.Proxy Proxy};
|
||||||
* when no proxy is available, the list will
|
* when no proxy is available, the list will
|
||||||
|
@ -91,7 +91,7 @@ public abstract class ResponseCache {
|
|||||||
/**
|
/**
|
||||||
* Sets (or unsets) the system-wide cache.
|
* Sets (or unsets) the system-wide cache.
|
||||||
*
|
*
|
||||||
* Note: non-standard procotol handlers may ignore this setting.
|
* Note: non-standard protocol handlers may ignore this setting.
|
||||||
*
|
*
|
||||||
* @param responseCache The response cache, or
|
* @param responseCache The response cache, or
|
||||||
* {@code null} to unset the cache.
|
* {@code null} to unset the cache.
|
||||||
|
@ -82,7 +82,7 @@ public abstract class SecureCacheResponse extends CacheResponse {
|
|||||||
* retrieved the network resource.
|
* retrieved the network resource.
|
||||||
*
|
*
|
||||||
* @return the server's principal. Returns an X500Principal of the
|
* @return the server's principal. Returns an X500Principal of the
|
||||||
* end-entity certiticate for X509-based cipher suites, and
|
* end-entity certificate for X509-based cipher suites, and
|
||||||
* KerberosPrincipal for Kerberos cipher suites.
|
* KerberosPrincipal for Kerberos cipher suites.
|
||||||
*
|
*
|
||||||
* @throws SSLPeerUnverifiedException if the peer was not verified.
|
* @throws SSLPeerUnverifiedException if the peer was not verified.
|
||||||
|
@ -166,7 +166,7 @@ class ServerSocket implements java.io.Closeable {
|
|||||||
* The {@code backlog} argument is the requested maximum number of
|
* The {@code backlog} argument is the requested maximum number of
|
||||||
* pending connections on the socket. Its exact semantics are implementation
|
* pending connections on the socket. Its exact semantics are implementation
|
||||||
* specific. In particular, an implementation may impose a maximum length
|
* specific. In particular, an implementation may impose a maximum length
|
||||||
* or may choose to ignore the parameter altogther. The value provided
|
* or may choose to ignore the parameter altogether. The value provided
|
||||||
* should be greater than {@code 0}. If it is less than or equal to
|
* should be greater than {@code 0}. If it is less than or equal to
|
||||||
* {@code 0}, then an implementation specific default will be used.
|
* {@code 0}, then an implementation specific default will be used.
|
||||||
*
|
*
|
||||||
@ -214,7 +214,7 @@ class ServerSocket implements java.io.Closeable {
|
|||||||
* The {@code backlog} argument is the requested maximum number of
|
* The {@code backlog} argument is the requested maximum number of
|
||||||
* pending connections on the socket. Its exact semantics are implementation
|
* pending connections on the socket. Its exact semantics are implementation
|
||||||
* specific. In particular, an implementation may impose a maximum length
|
* specific. In particular, an implementation may impose a maximum length
|
||||||
* or may choose to ignore the parameter altogther. The value provided
|
* or may choose to ignore the parameter altogether. The value provided
|
||||||
* should be greater than {@code 0}. If it is less than or equal to
|
* should be greater than {@code 0}. If it is less than or equal to
|
||||||
* {@code 0}, then an implementation specific default will be used.
|
* {@code 0}, then an implementation specific default will be used.
|
||||||
*
|
*
|
||||||
@ -351,7 +351,7 @@ class ServerSocket implements java.io.Closeable {
|
|||||||
* The {@code backlog} argument is the requested maximum number of
|
* The {@code backlog} argument is the requested maximum number of
|
||||||
* pending connections on the socket. Its exact semantics are implementation
|
* pending connections on the socket. Its exact semantics are implementation
|
||||||
* specific. In particular, an implementation may impose a maximum length
|
* specific. In particular, an implementation may impose a maximum length
|
||||||
* or may choose to ignore the parameter altogther. The value provided
|
* or may choose to ignore the parameter altogether. The value provided
|
||||||
* should be greater than {@code 0}. If it is less than or equal to
|
* should be greater than {@code 0}. If it is less than or equal to
|
||||||
* {@code 0}, then an implementation specific default will be used.
|
* {@code 0}, then an implementation specific default will be used.
|
||||||
* @param endpoint The IP address and port number to bind to.
|
* @param endpoint The IP address and port number to bind to.
|
||||||
@ -826,7 +826,7 @@ class ServerSocket implements java.io.Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is used both to
|
* The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is used both to
|
||||||
* set the size of the internal socket receive buffer, and to set the size
|
* set the size of the internal socket receive buffer, and to set the size
|
||||||
* of the TCP receive window that is advertized to the remote peer.
|
* of the TCP receive window that is advertised to the remote peer.
|
||||||
* <p>
|
* <p>
|
||||||
* It is possible to change the value subsequently, by calling
|
* It is possible to change the value subsequently, by calling
|
||||||
* {@link Socket#setReceiveBufferSize(int)}. However, if the application
|
* {@link Socket#setReceiveBufferSize(int)}. However, if the application
|
||||||
|
@ -1237,7 +1237,7 @@ class Socket implements java.io.Closeable {
|
|||||||
* should call {@link #getReceiveBufferSize()}.
|
* should call {@link #getReceiveBufferSize()}.
|
||||||
*
|
*
|
||||||
* <p>The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is also used
|
* <p>The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is also used
|
||||||
* to set the TCP receive window that is advertized to the remote peer.
|
* to set the TCP receive window that is advertised to the remote peer.
|
||||||
* Generally, the window size can be modified at any time when a socket is
|
* Generally, the window size can be modified at any time when a socket is
|
||||||
* connected. However, if a receive window larger than 64K is required then
|
* connected. However, if a receive window larger than 64K is required then
|
||||||
* this must be requested <B>before</B> the socket is connected to the
|
* this must be requested <B>before</B> the socket is connected to the
|
||||||
@ -1578,10 +1578,10 @@ class Socket implements java.io.Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Note: Closing a socket doesn't clear its connection state, which means
|
* Note: Closing a socket doesn't clear its connection state, which means
|
||||||
* this method will return {@code true} for a closed socket
|
* this method will return {@code true} for a closed socket
|
||||||
* (see {@link #isClosed()}) if it was successfuly connected prior
|
* (see {@link #isClosed()}) if it was successfully connected prior
|
||||||
* to being closed.
|
* to being closed.
|
||||||
*
|
*
|
||||||
* @return true if the socket was successfuly connected to a server
|
* @return true if the socket was successfully connected to a server
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
@ -1594,10 +1594,10 @@ class Socket implements java.io.Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Note: Closing a socket doesn't clear its binding state, which means
|
* Note: Closing a socket doesn't clear its binding state, which means
|
||||||
* this method will return {@code true} for a closed socket
|
* this method will return {@code true} for a closed socket
|
||||||
* (see {@link #isClosed()}) if it was successfuly bound prior
|
* (see {@link #isClosed()}) if it was successfully bound prior
|
||||||
* to being closed.
|
* to being closed.
|
||||||
*
|
*
|
||||||
* @return true if the socket was successfuly bound to an address
|
* @return true if the socket was successfully bound to an address
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
* @see #bind
|
* @see #bind
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +51,7 @@ class SocketOutputStream extends FileOutputStream {
|
|||||||
* Creates a new SocketOutputStream. Can only be called
|
* Creates a new SocketOutputStream. Can only be called
|
||||||
* by a Socket. This method needs to hang on to the owner Socket so
|
* by a Socket. This method needs to hang on to the owner Socket so
|
||||||
* that the fd will not be closed.
|
* that the fd will not be closed.
|
||||||
* @param impl the socket output stream inplemented
|
* @param impl the socket output stream implemented
|
||||||
*/
|
*/
|
||||||
SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
|
SocketOutputStream(AbstractPlainSocketImpl impl) throws IOException {
|
||||||
super(impl.getFileDescriptor());
|
super(impl.getFileDescriptor());
|
||||||
|
@ -317,7 +317,7 @@ public final class SocketPermission extends Permission
|
|||||||
if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) {
|
if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) {
|
||||||
/* More than one ":", meaning IPv6 address is not
|
/* More than one ":", meaning IPv6 address is not
|
||||||
* in RFC 2732 format;
|
* in RFC 2732 format;
|
||||||
* We will rectify user errors for all unambiguious cases
|
* We will rectify user errors for all unambiguous cases
|
||||||
*/
|
*/
|
||||||
StringTokenizer st = new StringTokenizer(host, ":");
|
StringTokenizer st = new StringTokenizer(host, ":");
|
||||||
int tokens = st.countTokens();
|
int tokens = st.countTokens();
|
||||||
@ -961,7 +961,7 @@ public final class SocketPermission extends Permission
|
|||||||
return (that.cname.endsWith(this.cname));
|
return (that.cname.endsWith(this.cname));
|
||||||
}
|
}
|
||||||
|
|
||||||
// comapare IP addresses
|
// compare IP addresses
|
||||||
if (this.addresses == null) {
|
if (this.addresses == null) {
|
||||||
this.getIP();
|
this.getIP();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
|
|||||||
private Socket cmdsock = null;
|
private Socket cmdsock = null;
|
||||||
private InputStream cmdIn = null;
|
private InputStream cmdIn = null;
|
||||||
private OutputStream cmdOut = null;
|
private OutputStream cmdOut = null;
|
||||||
/* true if the Proxy has been set programatically */
|
/* true if the Proxy has been set programmatically */
|
||||||
private boolean applicationSetProxy; /* false */
|
private boolean applicationSetProxy; /* false */
|
||||||
|
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the authentication machanism required by the proxy.
|
* Provides the authentication mechanism required by the proxy.
|
||||||
*/
|
*/
|
||||||
private boolean authenticate(byte method, InputStream in,
|
private boolean authenticate(byte method, InputStream in,
|
||||||
BufferedOutputStream out) throws IOException {
|
BufferedOutputStream out) throws IOException {
|
||||||
@ -158,7 +158,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
|
|||||||
// No Authentication required. We're done then!
|
// No Authentication required. We're done then!
|
||||||
if (method == NO_AUTH)
|
if (method == NO_AUTH)
|
||||||
return true;
|
return true;
|
||||||
/**
|
/*
|
||||||
* User/Password authentication. Try, in that order :
|
* User/Password authentication. Try, in that order :
|
||||||
* - The application provided Authenticator, if any
|
* - The application provided Authenticator, if any
|
||||||
* - the user.name & no password (backward compatibility behavior).
|
* - the user.name & no password (backward compatibility behavior).
|
||||||
@ -377,7 +377,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
|
|||||||
URI uri;
|
URI uri;
|
||||||
// Use getHostString() to avoid reverse lookups
|
// Use getHostString() to avoid reverse lookups
|
||||||
String host = epoint.getHostString();
|
String host = epoint.getHostString();
|
||||||
// IPv6 litteral?
|
// IPv6 literal?
|
||||||
if (epoint.getAddress() instanceof Inet6Address &&
|
if (epoint.getAddress() instanceof Inet6Address &&
|
||||||
(!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
|
(!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
|
||||||
host = "[" + host + "]";
|
host = "[" + host + "]";
|
||||||
@ -692,7 +692,7 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
|
|||||||
URI uri;
|
URI uri;
|
||||||
// Use getHostString() to avoid reverse lookups
|
// Use getHostString() to avoid reverse lookups
|
||||||
String host = saddr.getHostString();
|
String host = saddr.getHostString();
|
||||||
// IPv6 litteral?
|
// IPv6 literal?
|
||||||
if (saddr.getAddress() instanceof Inet6Address &&
|
if (saddr.getAddress() instanceof Inet6Address &&
|
||||||
(!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
|
(!host.startsWith("[")) && (host.indexOf(':') >= 0)) {
|
||||||
host = "[" + host + "]";
|
host = "[" + host + "]";
|
||||||
|
@ -861,9 +861,9 @@ public final class URI
|
|||||||
*
|
*
|
||||||
* <p> This method is provided for use in situations where it is known that
|
* <p> This method is provided for use in situations where it is known that
|
||||||
* the given string is a legal URI, for example for URI constants declared
|
* the given string is a legal URI, for example for URI constants declared
|
||||||
* within in a program, and so it would be considered a programming error
|
* within a program, and so it would be considered a programming error
|
||||||
* for the string not to parse as such. The constructors, which throw
|
* for the string not to parse as such. The constructors, which throw
|
||||||
* {@link URISyntaxException} directly, should be used situations where a
|
* {@link URISyntaxException} directly, should be used in situations where a
|
||||||
* URI is being constructed from user input or from some other source that
|
* URI is being constructed from user input or from some other source that
|
||||||
* may be prone to errors. </p>
|
* may be prone to errors. </p>
|
||||||
*
|
*
|
||||||
|
@ -1009,7 +1009,7 @@ public final class URL implements java.io.Serializable {
|
|||||||
* can not be converted to a URI.
|
* can not be converted to a URI.
|
||||||
*
|
*
|
||||||
* @exception URISyntaxException if this URL is not formatted strictly according to
|
* @exception URISyntaxException if this URL is not formatted strictly according to
|
||||||
* to RFC2396 and cannot be converted to a URI.
|
* RFC2396 and cannot be converted to a URI.
|
||||||
*
|
*
|
||||||
* @return a URI instance equivalent to this URL.
|
* @return a URI instance equivalent to this URL.
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
@ -1054,7 +1054,7 @@ public final class URL implements java.io.Serializable {
|
|||||||
/**
|
/**
|
||||||
* Same as {@link #openConnection()}, except that the connection will be
|
* Same as {@link #openConnection()}, except that the connection will be
|
||||||
* made through the specified proxy; Protocol handlers that do not
|
* made through the specified proxy; Protocol handlers that do not
|
||||||
* support proxing will ignore the proxy parameter and make a
|
* support proxying will ignore the proxy parameter and make a
|
||||||
* normal connection.
|
* normal connection.
|
||||||
*
|
*
|
||||||
* Invoking this method preempts the system's default
|
* Invoking this method preempts the system's default
|
||||||
|
@ -109,7 +109,7 @@ public class URLEncoder {
|
|||||||
* list. It is also noteworthy that this is consistent with
|
* list. It is also noteworthy that this is consistent with
|
||||||
* O'Reilly's "HTML: The Definitive Guide" (page 164).
|
* O'Reilly's "HTML: The Definitive Guide" (page 164).
|
||||||
*
|
*
|
||||||
* As a last note, Intenet Explorer does not encode the "@"
|
* As a last note, Internet Explorer does not encode the "@"
|
||||||
* character which is clearly not unreserved according to the
|
* character which is clearly not unreserved according to the
|
||||||
* RFC. We are being consistent with the RFC in this matter,
|
* RFC. We are being consistent with the RFC in this matter,
|
||||||
* as is Netscape.
|
* as is Netscape.
|
||||||
|
@ -51,7 +51,7 @@ import java.security.Permission;
|
|||||||
* portrange = portnumber | -portnumber | portnumber-[portnumber] | *
|
* portrange = portnumber | -portnumber | portnumber-[portnumber] | *
|
||||||
* hostrange = ([*.] dnsname) | IPv4address | IPv6address
|
* hostrange = ([*.] dnsname) | IPv4address | IPv6address
|
||||||
* </pre>
|
* </pre>
|
||||||
* <i>dnsname</i> is a standard DNS host or domain name, ie. one or more labels
|
* <i>dnsname</i> is a standard DNS host or domain name, i.e. one or more labels
|
||||||
* separated by ".". <i>IPv4address</i> is a standard literal IPv4 address and
|
* separated by ".". <i>IPv4address</i> is a standard literal IPv4 address and
|
||||||
* <i>IPv6address</i> is as defined in <a href="http://www.ietf.org/rfc/rfc2732.txt">
|
* <i>IPv6address</i> is as defined in <a href="http://www.ietf.org/rfc/rfc2732.txt">
|
||||||
* RFC 2732</a>. Literal IPv6 addresses must however, be enclosed in '[]' characters.
|
* RFC 2732</a>. Literal IPv6 addresses must however, be enclosed in '[]' characters.
|
||||||
@ -89,7 +89,7 @@ import java.security.Permission;
|
|||||||
* </tr>
|
* </tr>
|
||||||
* <tr><th scope="row">http://www.oracle.com/a/b/-</th>
|
* <tr><th scope="row">http://www.oracle.com/a/b/-</th>
|
||||||
* <td>The '-' character refers to all resources recursively below the
|
* <td>The '-' character refers to all resources recursively below the
|
||||||
* preceding path (eg. http://www.oracle.com/a/b/c/d/e.html matches this
|
* preceding path (e.g. http://www.oracle.com/a/b/c/d/e.html matches this
|
||||||
* example).
|
* example).
|
||||||
* </td>
|
* </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
|
@ -250,15 +250,15 @@ public abstract class URLStreamHandler {
|
|||||||
} else if (path != null && path.length() > 0) {
|
} else if (path != null && path.length() > 0) {
|
||||||
isRelPath = true;
|
isRelPath = true;
|
||||||
int ind = path.lastIndexOf('/');
|
int ind = path.lastIndexOf('/');
|
||||||
String seperator = "";
|
String separator = "";
|
||||||
if (ind == -1 && authority != null)
|
if (ind == -1 && authority != null)
|
||||||
seperator = "/";
|
separator = "/";
|
||||||
path = path.substring(0, ind + 1) + seperator +
|
path = path.substring(0, ind + 1) + separator +
|
||||||
spec.substring(start, limit);
|
spec.substring(start, limit);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
String seperator = (authority != null) ? "/" : "";
|
String separator = (authority != null) ? "/" : "";
|
||||||
path = seperator + spec.substring(start, limit);
|
path = separator + spec.substring(start, limit);
|
||||||
}
|
}
|
||||||
} else if (queryOnly && path != null) {
|
} else if (queryOnly && path != null) {
|
||||||
int ind = path.lastIndexOf('/');
|
int ind = path.lastIndexOf('/');
|
||||||
@ -314,7 +314,7 @@ public abstract class URLStreamHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default port for a URL parsed by this handler. This method
|
* Returns the default port for a URL parsed by this handler. This method
|
||||||
* is meant to be overidden by handlers with default port numbers.
|
* is meant to be overridden by handlers with default port numbers.
|
||||||
* @return the default port for a {@code URL} parsed by this handler.
|
* @return the default port for a {@code URL} parsed by this handler.
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
@ -323,14 +323,14 @@ public abstract class URLStreamHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the default equals calculation. May be overidden by handlers
|
* Provides the default equals calculation. May be overridden by handlers
|
||||||
* for other protocols that have different requirements for equals().
|
* for other protocols that have different requirements for equals().
|
||||||
* This method requires that none of its arguments is null. This is
|
* This method requires that none of its arguments is null. This is
|
||||||
* guaranteed by the fact that it is only called by java.net.URL class.
|
* guaranteed by the fact that it is only called by java.net.URL class.
|
||||||
* @param u1 a URL object
|
* @param u1 a URL object
|
||||||
* @param u2 a URL object
|
* @param u2 a URL object
|
||||||
* @return {@code true} if the two urls are
|
* @return {@code true} if the two urls are
|
||||||
* considered equal, ie. they refer to the same
|
* considered equal, i.e. they refer to the same
|
||||||
* fragment in the same file.
|
* fragment in the same file.
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
@ -342,7 +342,7 @@ public abstract class URLStreamHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the default hash calculation. May be overidden by handlers for
|
* Provides the default hash calculation. May be overridden by handlers for
|
||||||
* other protocols that have different requirements for hashCode
|
* other protocols that have different requirements for hashCode
|
||||||
* calculation.
|
* calculation.
|
||||||
* @param u a URL object
|
* @param u a URL object
|
||||||
|
@ -177,7 +177,7 @@ public interface MulticastChannel
|
|||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
* If a security manager is set, and its
|
* If a security manager is set, and its
|
||||||
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
|
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
|
||||||
* method denies access to the multiast group
|
* method denies access to the multicast group
|
||||||
*/
|
*/
|
||||||
MembershipKey join(InetAddress group, NetworkInterface interf)
|
MembershipKey join(InetAddress group, NetworkInterface interf)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
@ -226,7 +226,7 @@ public interface MulticastChannel
|
|||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
* If a security manager is set, and its
|
* If a security manager is set, and its
|
||||||
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
|
* {@link SecurityManager#checkMulticast(InetAddress) checkMulticast}
|
||||||
* method denies access to the multiast group
|
* method denies access to the multicast group
|
||||||
*/
|
*/
|
||||||
MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source)
|
MembershipKey join(InetAddress group, NetworkInterface interf, InetAddress source)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -153,7 +153,7 @@ public final class ModuleInfoExtender {
|
|||||||
|
|
||||||
ClassReader cr = new ClassReader(in);
|
ClassReader cr = new ClassReader(in);
|
||||||
|
|
||||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
|
||||||
@Override
|
@Override
|
||||||
public ModuleVisitor visitModule(String name, int flags, String version) {
|
public ModuleVisitor visitModule(String name, int flags, String version) {
|
||||||
Version v = ModuleInfoExtender.this.version;
|
Version v = ModuleInfoExtender.this.version;
|
||||||
@ -170,7 +170,7 @@ public final class ModuleInfoExtender {
|
|||||||
packages.forEach(pn -> mv.visitPackage(pn.replace('.', '/')));
|
packages.forEach(pn -> mv.visitPackage(pn.replace('.', '/')));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ModuleVisitor(Opcodes.ASM6, mv) {
|
return new ModuleVisitor(Opcodes.ASM7, mv) {
|
||||||
public void visitMainClass(String existingMainClass) {
|
public void visitMainClass(String existingMainClass) {
|
||||||
// skip main class if there is a new value
|
// skip main class if there is a new value
|
||||||
if (mainClass == null) {
|
if (mainClass == null) {
|
||||||
|
@ -59,9 +59,9 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visitor to visit a Java annotation. The methods of this class must be
|
* A visitor to visit a Java annotation. The methods of this class must be called in the following
|
||||||
* called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
|
* order: ( {@code visit} | {@code visitEnum} | {@code visitAnnotation} | {@code visitArray} )*
|
||||||
* <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
|
* {@code visitEnd}.
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
* @author Eugene Kuleshov
|
* @author Eugene Kuleshov
|
||||||
@ -69,23 +69,19 @@ package jdk.internal.org.objectweb.asm;
|
|||||||
public abstract class AnnotationVisitor {
|
public abstract class AnnotationVisitor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ASM API version implemented by this visitor. The value of this field
|
* The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
||||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
*/
|
*/
|
||||||
protected final int api;
|
protected final int api;
|
||||||
|
|
||||||
/**
|
/** The annotation visitor to which this visitor must delegate method calls. May be null. */
|
||||||
* The annotation visitor to which this visitor must delegate method calls.
|
|
||||||
* May be null.
|
|
||||||
*/
|
|
||||||
protected AnnotationVisitor av;
|
protected AnnotationVisitor av;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link AnnotationVisitor}.
|
* Constructs a new {@link AnnotationVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor(final int api) {
|
public AnnotationVisitor(final int api) {
|
||||||
this(api, null);
|
this(api, null);
|
||||||
@ -94,37 +90,31 @@ public abstract class AnnotationVisitor {
|
|||||||
/**
|
/**
|
||||||
* Constructs a new {@link AnnotationVisitor}.
|
* Constructs a new {@link AnnotationVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* @param annotationVisitor the annotation visitor to which this visitor must delegate method
|
||||||
* @param av
|
* calls. May be null.
|
||||||
* the annotation visitor to which this visitor must delegate
|
|
||||||
* method calls. May be null.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor(final int api, final AnnotationVisitor av) {
|
public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
|
||||||
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
|
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.av = av;
|
this.av = annotationVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a primitive value of the annotation.
|
* Visits a primitive value of the annotation.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name the value name.
|
||||||
* the value name.
|
* @param value the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link
|
||||||
* @param value
|
* Character}, {@link Short}, {@link Integer} , {@link Long}, {@link Float}, {@link Double},
|
||||||
* the actual value, whose type must be {@link Byte},
|
* {@link String} or {@link Type} of {@link Type#OBJECT} or {@link Type#ARRAY} sort. This
|
||||||
* {@link Boolean}, {@link Character}, {@link Short},
|
* value can also be an array of byte, boolean, short, char, int, long, float or double values
|
||||||
* {@link Integer} , {@link Long}, {@link Float}, {@link Double},
|
* (this is equivalent to using {@link #visitArray} and visiting each array element in turn,
|
||||||
* {@link String} or {@link Type} of OBJECT or ARRAY sort. This
|
* but is more convenient).
|
||||||
* value can also be an array of byte, boolean, short, char, int,
|
|
||||||
* long, float or double values (this is equivalent to using
|
|
||||||
* {@link #visitArray visitArray} and visiting each array element
|
|
||||||
* in turn, but is more convenient).
|
|
||||||
*/
|
*/
|
||||||
public void visit(String name, Object value) {
|
public void visit(final String name, final Object value) {
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
av.visit(name, value);
|
av.visit(name, value);
|
||||||
}
|
}
|
||||||
@ -133,63 +123,51 @@ public abstract class AnnotationVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits an enumeration value of the annotation.
|
* Visits an enumeration value of the annotation.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name the value name.
|
||||||
* the value name.
|
* @param descriptor the class descriptor of the enumeration class.
|
||||||
* @param desc
|
* @param value the actual enumeration value.
|
||||||
* the class descriptor of the enumeration class.
|
|
||||||
* @param value
|
|
||||||
* the actual enumeration value.
|
|
||||||
*/
|
*/
|
||||||
public void visitEnum(String name, String desc, String value) {
|
public void visitEnum(final String name, final String descriptor, final String value) {
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
av.visitEnum(name, desc, value);
|
av.visitEnum(name, descriptor, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a nested annotation value of the annotation.
|
* Visits a nested annotation value of the annotation.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name the value name.
|
||||||
* the value name.
|
* @param descriptor the class descriptor of the nested annotation class.
|
||||||
* @param desc
|
* @return a visitor to visit the actual nested annotation value, or {@literal null} if this
|
||||||
* the class descriptor of the nested annotation class.
|
* visitor is not interested in visiting this nested annotation. <i>The nested annotation
|
||||||
* @return a visitor to visit the actual nested annotation value, or
|
* value must be fully visited before calling other methods on this annotation visitor</i>.
|
||||||
* <tt>null</tt> if this visitor is not interested in visiting this
|
|
||||||
* nested annotation. <i>The nested annotation value must be fully
|
|
||||||
* visited before calling other methods on this annotation
|
|
||||||
* visitor</i>.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
return av.visitAnnotation(name, desc);
|
return av.visitAnnotation(name, descriptor);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits an array value of the annotation. Note that arrays of primitive
|
* Visits an array value of the annotation. Note that arrays of primitive types (such as byte,
|
||||||
* types (such as byte, boolean, short, char, int, long, float or double)
|
* boolean, short, char, int, long, float or double) can be passed as value to {@link #visit
|
||||||
* can be passed as value to {@link #visit visit}. This is what
|
* visit}. This is what {@link ClassReader} does.
|
||||||
* {@link ClassReader} does.
|
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name the value name.
|
||||||
* the value name.
|
* @return a visitor to visit the actual array value elements, or {@literal null} if this visitor
|
||||||
* @return a visitor to visit the actual array value elements, or
|
* is not interested in visiting these values. The 'name' parameters passed to the methods of
|
||||||
* <tt>null</tt> if this visitor is not interested in visiting these
|
* this visitor are ignored. <i>All the array values must be visited before calling other
|
||||||
* values. The 'name' parameters passed to the methods of this
|
* methods on this annotation visitor</i>.
|
||||||
* visitor are ignored. <i>All the array values must be visited
|
|
||||||
* before calling other methods on this annotation visitor</i>.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitArray(String name) {
|
public AnnotationVisitor visitArray(final String name) {
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
return av.visitArray(name);
|
return av.visitArray(name);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Visits the end of the annotation. */
|
||||||
* Visits the end of the annotation.
|
|
||||||
*/
|
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
if (av != null) {
|
if (av != null) {
|
||||||
av.visitEnd();
|
av.visitEnd();
|
||||||
|
@ -59,342 +59,391 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AnnotationVisitor} that generates annotations in bytecode form.
|
* An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation'
|
||||||
|
* structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter
|
||||||
|
* instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations
|
||||||
|
* attributes can be generated with the {@link #putAnnotations} method. Similarly, arrays of such
|
||||||
|
* lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes.
|
||||||
*
|
*
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16">JVMS
|
||||||
|
* 4.7.16</a>
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20">JVMS
|
||||||
|
* 4.7.20</a>
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
* @author Eugene Kuleshov
|
* @author Eugene Kuleshov
|
||||||
*/
|
*/
|
||||||
final class AnnotationWriter extends AnnotationVisitor {
|
final class AnnotationWriter extends AnnotationVisitor {
|
||||||
|
|
||||||
/**
|
/** Where the constants used in this AnnotationWriter must be stored. */
|
||||||
* The class writer to which this annotation must be added.
|
private final SymbolTable symbolTable;
|
||||||
*/
|
|
||||||
private final ClassWriter cw;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of values in this annotation.
|
* Whether values are named or not. AnnotationWriter instances used for annotation default and
|
||||||
|
* annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each
|
||||||
|
* value, instead of an element_name_index followed by an element_value).
|
||||||
*/
|
*/
|
||||||
private int size;
|
private final boolean useNamedValues;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
|
* The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values
|
||||||
* writers used for annotation default and annotation arrays use unnamed
|
* visited so far. All the fields of these structures, except the last one - the
|
||||||
* values.
|
* element_value_pairs array, must be set before this ByteVector is passed to the constructor
|
||||||
|
* (num_element_value_pairs can be set to 0, it is reset to the correct value in {@link
|
||||||
|
* #visitEnd()}). The element_value_pairs array is filled incrementally in the various visit()
|
||||||
|
* methods.
|
||||||
|
*
|
||||||
|
* <p>Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a
|
||||||
|
* single element_value by definition), this ByteVector is initially empty when passed to the
|
||||||
|
* constructor, and {@link #numElementValuePairsOffset} is set to -1.
|
||||||
*/
|
*/
|
||||||
private final boolean named;
|
private final ByteVector annotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The annotation values in bytecode form. This byte vector only contains
|
* The offset in {@link #annotation} where {@link #numElementValuePairs} must be stored (or -1 for
|
||||||
* the values themselves, i.e. the number of values must be stored as a
|
* the case of AnnotationDefault attributes).
|
||||||
* unsigned short just before these bytes.
|
|
||||||
*/
|
*/
|
||||||
private final ByteVector bv;
|
private final int numElementValuePairsOffset;
|
||||||
|
|
||||||
|
/** The number of element value pairs visited so far. */
|
||||||
|
private int numElementValuePairs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The byte vector to be used to store the number of values of this
|
* The previous AnnotationWriter. This field is used to store the list of annotations of a
|
||||||
* annotation. See {@link #bv}.
|
* Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
|
||||||
|
* (annotation values of annotation type), or for AnnotationDefault attributes.
|
||||||
*/
|
*/
|
||||||
private final ByteVector parent;
|
private final AnnotationWriter previousAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Where the number of values of this annotation must be stored in
|
* The next AnnotationWriter. This field is used to store the list of annotations of a
|
||||||
* {@link #parent}.
|
* Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations
|
||||||
|
* (annotation values of annotation type), or for AnnotationDefault attributes.
|
||||||
*/
|
*/
|
||||||
private final int offset;
|
private AnnotationWriter nextAnnotation;
|
||||||
|
|
||||||
/**
|
// -----------------------------------------------------------------------------------------------
|
||||||
* Next annotation writer. This field is used to store annotation lists.
|
// Constructors
|
||||||
*/
|
// -----------------------------------------------------------------------------------------------
|
||||||
AnnotationWriter next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Previous annotation writer. This field is used to store annotation lists.
|
|
||||||
*/
|
|
||||||
AnnotationWriter prev;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Constructor
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link AnnotationWriter}.
|
* Constructs a new {@link AnnotationWriter}.
|
||||||
*
|
*
|
||||||
* @param cw
|
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
|
||||||
* the class writer to which this annotation must be added.
|
* @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays
|
||||||
* @param named
|
* use unnamed values.
|
||||||
* <tt>true<tt> if values are named, <tt>false</tt> otherwise.
|
* @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
|
||||||
* @param bv
|
* the visited content must be stored. This ByteVector must already contain all the fields of
|
||||||
* where the annotation values must be stored.
|
* the structure except the last one (the element_value_pairs array).
|
||||||
* @param parent
|
* @param previousAnnotation the previously visited annotation of the
|
||||||
* where the number of annotation values must be stored.
|
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
|
||||||
* @param offset
|
* other cases (e.g. nested or array annotations).
|
||||||
* where in <tt>parent</tt> the number of annotation values must
|
|
||||||
* be stored.
|
|
||||||
*/
|
*/
|
||||||
AnnotationWriter(final ClassWriter cw, final boolean named,
|
AnnotationWriter(
|
||||||
final ByteVector bv, final ByteVector parent, final int offset) {
|
final SymbolTable symbolTable,
|
||||||
super(Opcodes.ASM6);
|
final boolean useNamedValues,
|
||||||
this.cw = cw;
|
final ByteVector annotation,
|
||||||
this.named = named;
|
final AnnotationWriter previousAnnotation) {
|
||||||
this.bv = bv;
|
super(Opcodes.ASM7);
|
||||||
this.parent = parent;
|
this.symbolTable = symbolTable;
|
||||||
this.offset = offset;
|
this.useNamedValues = useNamedValues;
|
||||||
|
this.annotation = annotation;
|
||||||
|
// By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'.
|
||||||
|
this.numElementValuePairsOffset = annotation.length == 0 ? -1 : annotation.length - 2;
|
||||||
|
this.previousAnnotation = previousAnnotation;
|
||||||
|
if (previousAnnotation != null) {
|
||||||
|
previousAnnotation.nextAnnotation = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
/**
|
||||||
|
* Constructs a new {@link AnnotationWriter} using named values.
|
||||||
|
*
|
||||||
|
* @param symbolTable where the constants used in this AnnotationWriter must be stored.
|
||||||
|
* @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to
|
||||||
|
* the visited content must be stored. This ByteVector must already contain all the fields of
|
||||||
|
* the structure except the last one (the element_value_pairs array).
|
||||||
|
* @param previousAnnotation the previously visited annotation of the
|
||||||
|
* Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in
|
||||||
|
* other cases (e.g. nested or array annotations).
|
||||||
|
*/
|
||||||
|
AnnotationWriter(
|
||||||
|
final SymbolTable symbolTable,
|
||||||
|
final ByteVector annotation,
|
||||||
|
final AnnotationWriter previousAnnotation) {
|
||||||
|
this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Implementation of the AnnotationVisitor abstract class
|
// Implementation of the AnnotationVisitor abstract class
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(final String name, final Object value) {
|
public void visit(final String name, final Object value) {
|
||||||
++size;
|
// Case of an element_value with a const_value_index, class_info_index or array_index field.
|
||||||
if (named) {
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||||||
bv.putShort(cw.newUTF8(name));
|
++numElementValuePairs;
|
||||||
|
if (useNamedValues) {
|
||||||
|
annotation.putShort(symbolTable.addConstantUtf8(name));
|
||||||
}
|
}
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
bv.put12('s', cw.newUTF8((String) value));
|
annotation.put12('s', symbolTable.addConstantUtf8((String) value));
|
||||||
} else if (value instanceof Byte) {
|
} else if (value instanceof Byte) {
|
||||||
bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
|
annotation.put12('B', symbolTable.addConstantInteger(((Byte) value).byteValue()).index);
|
||||||
} else if (value instanceof Boolean) {
|
} else if (value instanceof Boolean) {
|
||||||
int v = ((Boolean) value).booleanValue() ? 1 : 0;
|
int booleanValue = ((Boolean) value).booleanValue() ? 1 : 0;
|
||||||
bv.put12('Z', cw.newInteger(v).index);
|
annotation.put12('Z', symbolTable.addConstantInteger(booleanValue).index);
|
||||||
} else if (value instanceof Character) {
|
} else if (value instanceof Character) {
|
||||||
bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
|
annotation.put12('C', symbolTable.addConstantInteger(((Character) value).charValue()).index);
|
||||||
} else if (value instanceof Short) {
|
} else if (value instanceof Short) {
|
||||||
bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
|
annotation.put12('S', symbolTable.addConstantInteger(((Short) value).shortValue()).index);
|
||||||
} else if (value instanceof Type) {
|
} else if (value instanceof Type) {
|
||||||
bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
|
annotation.put12('c', symbolTable.addConstantUtf8(((Type) value).getDescriptor()));
|
||||||
} else if (value instanceof byte[]) {
|
} else if (value instanceof byte[]) {
|
||||||
byte[] v = (byte[]) value;
|
byte[] byteArray = (byte[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', byteArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (byte byteValue : byteArray) {
|
||||||
bv.put12('B', cw.newInteger(v[i]).index);
|
annotation.put12('B', symbolTable.addConstantInteger(byteValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof boolean[]) {
|
} else if (value instanceof boolean[]) {
|
||||||
boolean[] v = (boolean[]) value;
|
boolean[] booleanArray = (boolean[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', booleanArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (boolean booleanValue : booleanArray) {
|
||||||
bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
|
annotation.put12('Z', symbolTable.addConstantInteger(booleanValue ? 1 : 0).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof short[]) {
|
} else if (value instanceof short[]) {
|
||||||
short[] v = (short[]) value;
|
short[] shortArray = (short[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', shortArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (short shortValue : shortArray) {
|
||||||
bv.put12('S', cw.newInteger(v[i]).index);
|
annotation.put12('S', symbolTable.addConstantInteger(shortValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof char[]) {
|
} else if (value instanceof char[]) {
|
||||||
char[] v = (char[]) value;
|
char[] charArray = (char[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', charArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (char charValue : charArray) {
|
||||||
bv.put12('C', cw.newInteger(v[i]).index);
|
annotation.put12('C', symbolTable.addConstantInteger(charValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof int[]) {
|
} else if (value instanceof int[]) {
|
||||||
int[] v = (int[]) value;
|
int[] intArray = (int[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', intArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (int intValue : intArray) {
|
||||||
bv.put12('I', cw.newInteger(v[i]).index);
|
annotation.put12('I', symbolTable.addConstantInteger(intValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof long[]) {
|
} else if (value instanceof long[]) {
|
||||||
long[] v = (long[]) value;
|
long[] longArray = (long[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', longArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (long longValue : longArray) {
|
||||||
bv.put12('J', cw.newLong(v[i]).index);
|
annotation.put12('J', symbolTable.addConstantLong(longValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof float[]) {
|
} else if (value instanceof float[]) {
|
||||||
float[] v = (float[]) value;
|
float[] floatArray = (float[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', floatArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (float floatValue : floatArray) {
|
||||||
bv.put12('F', cw.newFloat(v[i]).index);
|
annotation.put12('F', symbolTable.addConstantFloat(floatValue).index);
|
||||||
}
|
}
|
||||||
} else if (value instanceof double[]) {
|
} else if (value instanceof double[]) {
|
||||||
double[] v = (double[]) value;
|
double[] doubleArray = (double[]) value;
|
||||||
bv.put12('[', v.length);
|
annotation.put12('[', doubleArray.length);
|
||||||
for (int i = 0; i < v.length; i++) {
|
for (double doubleValue : doubleArray) {
|
||||||
bv.put12('D', cw.newDouble(v[i]).index);
|
annotation.put12('D', symbolTable.addConstantDouble(doubleValue).index);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Item i = cw.newConstItem(value);
|
Symbol symbol = symbolTable.addConstant(value);
|
||||||
bv.put12(".s.IFJDCS".charAt(i.type), i.index);
|
annotation.put12(".s.IFJDCS".charAt(symbol.tag), symbol.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnum(final String name, final String desc,
|
public void visitEnum(final String name, final String descriptor, final String value) {
|
||||||
final String value) {
|
// Case of an element_value with an enum_const_value field.
|
||||||
++size;
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||||||
if (named) {
|
++numElementValuePairs;
|
||||||
bv.putShort(cw.newUTF8(name));
|
if (useNamedValues) {
|
||||||
|
annotation.putShort(symbolTable.addConstantUtf8(name));
|
||||||
}
|
}
|
||||||
bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
|
annotation
|
||||||
|
.put12('e', symbolTable.addConstantUtf8(descriptor))
|
||||||
|
.putShort(symbolTable.addConstantUtf8(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String name,
|
public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
|
||||||
final String desc) {
|
// Case of an element_value with an annotation_value field.
|
||||||
++size;
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1.
|
||||||
if (named) {
|
++numElementValuePairs;
|
||||||
bv.putShort(cw.newUTF8(name));
|
if (useNamedValues) {
|
||||||
|
annotation.putShort(symbolTable.addConstantUtf8(name));
|
||||||
}
|
}
|
||||||
// write tag and type, and reserve space for values count
|
// Write tag and type_index, and reserve 2 bytes for num_element_value_pairs.
|
||||||
bv.put12('@', cw.newUTF8(desc)).putShort(0);
|
annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||||||
return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
|
return new AnnotationWriter(symbolTable, annotation, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitArray(final String name) {
|
public AnnotationVisitor visitArray(final String name) {
|
||||||
++size;
|
// Case of an element_value with an array_value field.
|
||||||
if (named) {
|
// https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1
|
||||||
bv.putShort(cw.newUTF8(name));
|
++numElementValuePairs;
|
||||||
|
if (useNamedValues) {
|
||||||
|
annotation.putShort(symbolTable.addConstantUtf8(name));
|
||||||
}
|
}
|
||||||
// write tag, and reserve space for array size
|
// Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the
|
||||||
bv.put12('[', 0);
|
// end of an element_value of array type is similar to the end of an 'annotation' structure: an
|
||||||
return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
|
// unsigned short num_values followed by num_values element_value, versus an unsigned short
|
||||||
|
// num_element_value_pairs, followed by num_element_value_pairs { element_name_index,
|
||||||
|
// element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to
|
||||||
|
// visit the array elements. Its num_element_value_pairs will correspond to the number of array
|
||||||
|
// elements and will be stored in what is in fact num_values.
|
||||||
|
annotation.put12('[', 0);
|
||||||
|
return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
if (parent != null) {
|
if (numElementValuePairsOffset != -1) {
|
||||||
byte[] data = parent.data;
|
byte[] data = annotation.data;
|
||||||
data[offset] = (byte) (size >>> 8);
|
data[numElementValuePairsOffset] = (byte) (numElementValuePairs >>> 8);
|
||||||
data[offset + 1] = (byte) size;
|
data[numElementValuePairsOffset + 1] = (byte) numElementValuePairs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Utility methods
|
// Utility methods
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of this annotation writer list.
|
* Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation
|
||||||
|
* and all its <i>predecessors</i> (see {@link #previousAnnotation}. Also adds the attribute name
|
||||||
|
* to the constant pool of the class (if not null).
|
||||||
*
|
*
|
||||||
* @return the size of this annotation writer list.
|
* @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null.
|
||||||
|
* @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this
|
||||||
|
* annotation and all its predecessors. This includes the size of the attribute_name_index and
|
||||||
|
* attribute_length fields.
|
||||||
*/
|
*/
|
||||||
int getSize() {
|
int computeAnnotationsSize(final String attributeName) {
|
||||||
int size = 0;
|
if (attributeName != null) {
|
||||||
AnnotationWriter aw = this;
|
symbolTable.addConstantUtf8(attributeName);
|
||||||
while (aw != null) {
|
|
||||||
size += aw.bv.length;
|
|
||||||
aw = aw.next;
|
|
||||||
}
|
}
|
||||||
return size;
|
// The attribute_name_index, attribute_length and num_annotations fields use 8 bytes.
|
||||||
|
int attributeSize = 8;
|
||||||
|
AnnotationWriter annotationWriter = this;
|
||||||
|
while (annotationWriter != null) {
|
||||||
|
attributeSize += annotationWriter.annotation.length;
|
||||||
|
annotationWriter = annotationWriter.previousAnnotation;
|
||||||
|
}
|
||||||
|
return attributeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts the annotations of this annotation writer list into the given byte
|
* Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its
|
||||||
* vector.
|
* <i>predecessors</i> (see {@link #previousAnnotation} in the given ByteVector. Annotations are
|
||||||
|
* put in the same order they have been visited.
|
||||||
*
|
*
|
||||||
* @param out
|
* @param attributeNameIndex the constant pool index of the attribute name (one of
|
||||||
* where the annotations must be put.
|
* "Runtime[In]Visible[Type]Annotations").
|
||||||
|
* @param output where the attribute must be put.
|
||||||
*/
|
*/
|
||||||
void put(final ByteVector out) {
|
void putAnnotations(final int attributeNameIndex, final ByteVector output) {
|
||||||
int n = 0;
|
int attributeLength = 2; // For num_annotations.
|
||||||
int size = 2;
|
int numAnnotations = 0;
|
||||||
AnnotationWriter aw = this;
|
AnnotationWriter annotationWriter = this;
|
||||||
AnnotationWriter last = null;
|
AnnotationWriter firstAnnotation = null;
|
||||||
while (aw != null) {
|
while (annotationWriter != null) {
|
||||||
++n;
|
// In case the user forgot to call visitEnd().
|
||||||
size += aw.bv.length;
|
annotationWriter.visitEnd();
|
||||||
aw.visitEnd(); // in case user forgot to call visitEnd
|
attributeLength += annotationWriter.annotation.length;
|
||||||
aw.prev = last;
|
numAnnotations++;
|
||||||
last = aw;
|
firstAnnotation = annotationWriter;
|
||||||
aw = aw.next;
|
annotationWriter = annotationWriter.previousAnnotation;
|
||||||
}
|
}
|
||||||
out.putInt(size);
|
output.putShort(attributeNameIndex);
|
||||||
out.putShort(n);
|
output.putInt(attributeLength);
|
||||||
aw = last;
|
output.putShort(numAnnotations);
|
||||||
while (aw != null) {
|
annotationWriter = firstAnnotation;
|
||||||
out.putByteArray(aw.bv.data, 0, aw.bv.length);
|
while (annotationWriter != null) {
|
||||||
aw = aw.prev;
|
output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
|
||||||
|
annotationWriter = annotationWriter.nextAnnotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts the given annotation lists into the given byte vector.
|
* Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the
|
||||||
|
* annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the
|
||||||
|
* constant pool of the class.
|
||||||
*
|
*
|
||||||
* @param panns
|
* @param attributeName one of "Runtime[In]VisibleParameterAnnotations".
|
||||||
* an array of annotation writer lists.
|
* @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
|
||||||
* @param off
|
* element).
|
||||||
* index of the first annotation to be written.
|
* @param annotableParameterCount the number of elements in annotationWriters to take into account
|
||||||
* @param out
|
* (elements [0..annotableParameterCount[ are taken into account).
|
||||||
* where the annotations must be put.
|
* @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding
|
||||||
|
* to the given sub-array of AnnotationWriter lists. This includes the size of the
|
||||||
|
* attribute_name_index and attribute_length fields.
|
||||||
*/
|
*/
|
||||||
static void put(final AnnotationWriter[] panns, final int off,
|
static int computeParameterAnnotationsSize(
|
||||||
final ByteVector out) {
|
final String attributeName,
|
||||||
int size = 1 + 2 * (panns.length - off);
|
final AnnotationWriter[] annotationWriters,
|
||||||
for (int i = off; i < panns.length; ++i) {
|
final int annotableParameterCount) {
|
||||||
size += panns[i] == null ? 0 : panns[i].getSize();
|
// Note: attributeName is added to the constant pool by the call to computeAnnotationsSize
|
||||||
}
|
// below. This assumes that there is at least one non-null element in the annotationWriters
|
||||||
out.putInt(size).putByte(panns.length - off);
|
// sub-array (which is ensured by the lazy instantiation of this array in MethodWriter).
|
||||||
for (int i = off; i < panns.length; ++i) {
|
// The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each
|
||||||
AnnotationWriter aw = panns[i];
|
// element of the parameter_annotations array uses 2 bytes for its num_annotations field.
|
||||||
AnnotationWriter last = null;
|
int attributeSize = 7 + 2 * annotableParameterCount;
|
||||||
int n = 0;
|
for (int i = 0; i < annotableParameterCount; ++i) {
|
||||||
while (aw != null) {
|
AnnotationWriter annotationWriter = annotationWriters[i];
|
||||||
++n;
|
attributeSize +=
|
||||||
aw.visitEnd(); // in case user forgot to call visitEnd
|
annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(attributeName) - 8;
|
||||||
aw.prev = last;
|
|
||||||
last = aw;
|
|
||||||
aw = aw.next;
|
|
||||||
}
|
|
||||||
out.putShort(n);
|
|
||||||
aw = last;
|
|
||||||
while (aw != null) {
|
|
||||||
out.putByteArray(aw.bv.data, 0, aw.bv.length);
|
|
||||||
aw = aw.prev;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return attributeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts the given type reference and type path into the given bytevector.
|
* Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists
|
||||||
* LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported.
|
* from the given AnnotationWriter sub-array in the given ByteVector.
|
||||||
*
|
*
|
||||||
* @param typeRef
|
* @param attributeNameIndex constant pool index of the attribute name (one of
|
||||||
* a reference to the annotated type. See {@link TypeReference}.
|
* Runtime[In]VisibleParameterAnnotations).
|
||||||
* @param typePath
|
* @param annotationWriters an array of AnnotationWriter lists (designated by their <i>last</i>
|
||||||
* the path to the annotated type argument, wildcard bound, array
|
* element).
|
||||||
* element type, or static inner type within 'typeRef'. May be
|
* @param annotableParameterCount the number of elements in annotationWriters to put (elements
|
||||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
* [0..annotableParameterCount[ are put).
|
||||||
* @param out
|
* @param output where the attribute must be put.
|
||||||
* where the type reference and type path must be put.
|
|
||||||
*/
|
*/
|
||||||
static void putTarget(int typeRef, TypePath typePath, ByteVector out) {
|
static void putParameterAnnotations(
|
||||||
switch (typeRef >>> 24) {
|
final int attributeNameIndex,
|
||||||
case 0x00: // CLASS_TYPE_PARAMETER
|
final AnnotationWriter[] annotationWriters,
|
||||||
case 0x01: // METHOD_TYPE_PARAMETER
|
final int annotableParameterCount,
|
||||||
case 0x16: // METHOD_FORMAL_PARAMETER
|
final ByteVector output) {
|
||||||
out.putShort(typeRef >>> 16);
|
// The num_parameters field uses 1 byte, and each element of the parameter_annotations array
|
||||||
break;
|
// uses 2 bytes for its num_annotations field.
|
||||||
case 0x13: // FIELD
|
int attributeLength = 1 + 2 * annotableParameterCount;
|
||||||
case 0x14: // METHOD_RETURN
|
for (int i = 0; i < annotableParameterCount; ++i) {
|
||||||
case 0x15: // METHOD_RECEIVER
|
AnnotationWriter annotationWriter = annotationWriters[i];
|
||||||
out.putByte(typeRef >>> 24);
|
attributeLength +=
|
||||||
break;
|
annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8;
|
||||||
case 0x47: // CAST
|
}
|
||||||
case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
|
output.putShort(attributeNameIndex);
|
||||||
case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT
|
output.putInt(attributeLength);
|
||||||
case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
|
output.putByte(annotableParameterCount);
|
||||||
case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT
|
for (int i = 0; i < annotableParameterCount; ++i) {
|
||||||
out.putInt(typeRef);
|
AnnotationWriter annotationWriter = annotationWriters[i];
|
||||||
break;
|
AnnotationWriter firstAnnotation = null;
|
||||||
// case 0x10: // CLASS_EXTENDS
|
int numAnnotations = 0;
|
||||||
// case 0x11: // CLASS_TYPE_PARAMETER_BOUND
|
while (annotationWriter != null) {
|
||||||
// case 0x12: // METHOD_TYPE_PARAMETER_BOUND
|
// In case user the forgot to call visitEnd().
|
||||||
// case 0x17: // THROWS
|
annotationWriter.visitEnd();
|
||||||
// case 0x42: // EXCEPTION_PARAMETER
|
numAnnotations++;
|
||||||
// case 0x43: // INSTANCEOF
|
firstAnnotation = annotationWriter;
|
||||||
// case 0x44: // NEW
|
annotationWriter = annotationWriter.previousAnnotation;
|
||||||
// case 0x45: // CONSTRUCTOR_REFERENCE
|
}
|
||||||
// case 0x46: // METHOD_REFERENCE
|
output.putShort(numAnnotations);
|
||||||
default:
|
annotationWriter = firstAnnotation;
|
||||||
out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8);
|
while (annotationWriter != null) {
|
||||||
break;
|
output.putByteArray(
|
||||||
|
annotationWriter.annotation.data, 0, annotationWriter.annotation.length);
|
||||||
|
annotationWriter = annotationWriter.nextAnnotation;
|
||||||
}
|
}
|
||||||
if (typePath == null) {
|
|
||||||
out.putByte(0);
|
|
||||||
} else {
|
|
||||||
int length = typePath.b[typePath.offset] * 2 + 1;
|
|
||||||
out.putByteArray(typePath.b, typePath.offset, length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,55 +58,62 @@
|
|||||||
*/
|
*/
|
||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A non standard class, field, method or code attribute.
|
* A non standard class, field, method or code attribute, as defined in the Java Virtual Machine
|
||||||
|
* Specification (JVMS).
|
||||||
*
|
*
|
||||||
|
* @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7">JVMS
|
||||||
|
* 4.7</a>
|
||||||
|
* @see <a href= "https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS
|
||||||
|
* 4.7.3</a>
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
* @author Eugene Kuleshov
|
* @author Eugene Kuleshov
|
||||||
*/
|
*/
|
||||||
public class Attribute {
|
public class Attribute {
|
||||||
|
|
||||||
/**
|
/** The type of this attribute, also called its name in the JVMS. */
|
||||||
* The type of this attribute.
|
|
||||||
*/
|
|
||||||
public final String type;
|
public final String type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The raw value of this attribute, used only for unknown attributes.
|
* The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}).
|
||||||
|
* The 6 header bytes of the attribute (attribute_name_index and attribute_length) are <i>not</i>
|
||||||
|
* included.
|
||||||
*/
|
*/
|
||||||
byte[] value;
|
private byte[] content;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The next attribute in this attribute list. May be <tt>null</tt>.
|
* The next attribute in this attribute list (Attribute instances can be linked via this field to
|
||||||
|
* store a list of class, field, method or code attributes). May be {@literal null}.
|
||||||
*/
|
*/
|
||||||
Attribute next;
|
Attribute nextAttribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new empty attribute.
|
* Constructs a new empty attribute.
|
||||||
*
|
*
|
||||||
* @param type
|
* @param type the type of the attribute.
|
||||||
* the type of the attribute.
|
|
||||||
*/
|
*/
|
||||||
protected Attribute(final String type) {
|
protected Attribute(final String type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <tt>true</tt> if this type of attribute is unknown. The default
|
* Returns {@literal true} if this type of attribute is unknown. This means that the attribute
|
||||||
* implementation of this method always returns <tt>true</tt>.
|
* content can't be parsed to extract constant pool references, labels, etc. Instead, the
|
||||||
|
* attribute content is read as an opaque byte array, and written back as is. This can lead to
|
||||||
|
* invalid attributes, if the content actually contains constant pool references, labels, or other
|
||||||
|
* symbolic references that need to be updated when there are changes to the constant pool, the
|
||||||
|
* method bytecode, etc. The default implementation of this method always returns {@literal true}.
|
||||||
*
|
*
|
||||||
* @return <tt>true</tt> if this type of attribute is unknown.
|
* @return {@literal true} if this type of attribute is unknown.
|
||||||
*/
|
*/
|
||||||
public boolean isUnknown() {
|
public boolean isUnknown() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <tt>true</tt> if this type of attribute is a code attribute.
|
* Returns {@literal true} if this type of attribute is a code attribute.
|
||||||
*
|
*
|
||||||
* @return <tt>true</tt> if this type of attribute is a code attribute.
|
* @return {@literal true} if this type of attribute is a code attribute.
|
||||||
*/
|
*/
|
||||||
public boolean isCodeAttribute() {
|
public boolean isCodeAttribute() {
|
||||||
return false;
|
return false;
|
||||||
@ -115,240 +122,235 @@ public class Attribute {
|
|||||||
/**
|
/**
|
||||||
* Returns the labels corresponding to this attribute.
|
* Returns the labels corresponding to this attribute.
|
||||||
*
|
*
|
||||||
* @return the labels corresponding to this attribute, or <tt>null</tt> if
|
* @return the labels corresponding to this attribute, or {@literal null} if this attribute is not
|
||||||
* this attribute is not a code attribute that contains labels.
|
* a code attribute that contains labels.
|
||||||
*/
|
*/
|
||||||
protected Label[] getLabels() {
|
protected Label[] getLabels() {
|
||||||
return null;
|
return new Label[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a {@link #type type} attribute. This method must return a
|
* Reads a {@link #type} attribute. This method must return a <i>new</i> {@link Attribute} object,
|
||||||
* <i>new</i> {@link Attribute} object, of type {@link #type type},
|
* of type {@link #type}, corresponding to the 'length' bytes starting at 'offset', in the given
|
||||||
* corresponding to the <tt>len</tt> bytes starting at the given offset, in
|
* ClassReader.
|
||||||
* the given class reader.
|
|
||||||
*
|
*
|
||||||
* @param cr
|
* @param classReader the class that contains the attribute to be read.
|
||||||
* the class that contains the attribute to be read.
|
* @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
|
||||||
* @param off
|
* 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
|
||||||
* index of the first byte of the attribute's content in
|
* account here.
|
||||||
* {@link ClassReader#b cr.b}. The 6 attribute header bytes,
|
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
|
||||||
* containing the type and the length of the attribute, are not
|
* @param charBuffer the buffer to be used to call the ClassReader methods requiring a
|
||||||
* taken into account here.
|
* 'charBuffer' parameter.
|
||||||
* @param len
|
* @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
|
||||||
* the length of the attribute's content.
|
* in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
|
||||||
* @param buf
|
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
|
||||||
* buffer to be used to call {@link ClassReader#readUTF8
|
* account here.
|
||||||
* readUTF8}, {@link ClassReader#readClass(int,char[]) readClass}
|
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
|
||||||
* or {@link ClassReader#readConst readConst}.
|
|
||||||
* @param codeOff
|
|
||||||
* index of the first byte of code's attribute content in
|
|
||||||
* {@link ClassReader#b cr.b}, or -1 if the attribute to be read
|
|
||||||
* is not a code attribute. The 6 attribute header bytes,
|
|
||||||
* containing the type and the length of the attribute, are not
|
|
||||||
* taken into account here.
|
|
||||||
* @param labels
|
|
||||||
* the labels of the method's code, or <tt>null</tt> if the
|
|
||||||
* attribute to be read is not a code attribute.
|
|
||||||
* @return a <i>new</i> {@link Attribute} object corresponding to the given
|
|
||||||
* bytes.
|
|
||||||
*/
|
|
||||||
protected Attribute read(final ClassReader cr, final int off,
|
|
||||||
final int len, final char[] buf, final int codeOff,
|
|
||||||
final Label[] labels) {
|
|
||||||
Attribute attr = new Attribute(type);
|
|
||||||
attr.value = new byte[len];
|
|
||||||
System.arraycopy(cr.b, off, attr.value, 0, len);
|
|
||||||
return attr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the byte array form of this attribute.
|
|
||||||
*
|
|
||||||
* @param cw
|
|
||||||
* the class to which this attribute must be added. This
|
|
||||||
* parameter can be used to add to the constant pool of this
|
|
||||||
* class the items that corresponds to this attribute.
|
|
||||||
* @param code
|
|
||||||
* the bytecode of the method corresponding to this code
|
|
||||||
* attribute, or <tt>null</tt> if this attribute is not a code
|
|
||||||
* attributes.
|
|
||||||
* @param len
|
|
||||||
* the length of the bytecode of the method corresponding to this
|
|
||||||
* code attribute, or <tt>null</tt> if this attribute is not a
|
|
||||||
* code attribute.
|
|
||||||
* @param maxStack
|
|
||||||
* the maximum stack size of the method corresponding to this
|
|
||||||
* code attribute, or -1 if this attribute is not a code
|
|
||||||
* attribute.
|
|
||||||
* @param maxLocals
|
|
||||||
* the maximum number of local variables of the method
|
|
||||||
* corresponding to this code attribute, or -1 if this attribute
|
|
||||||
* is not a code attribute.
|
* is not a code attribute.
|
||||||
|
* @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
|
||||||
|
*/
|
||||||
|
protected Attribute read(
|
||||||
|
final ClassReader classReader,
|
||||||
|
final int offset,
|
||||||
|
final int length,
|
||||||
|
final char[] charBuffer,
|
||||||
|
final int codeAttributeOffset,
|
||||||
|
final Label[] labels) {
|
||||||
|
Attribute attribute = new Attribute(type);
|
||||||
|
attribute.content = new byte[length];
|
||||||
|
System.arraycopy(classReader.b, offset, attribute.content, 0, length);
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the byte array form of the content of this attribute. The 6 header bytes
|
||||||
|
* (attribute_name_index and attribute_length) must <i>not</i> be added in the returned
|
||||||
|
* ByteVector.
|
||||||
|
*
|
||||||
|
* @param classWriter the class to which this attribute must be added. This parameter can be used
|
||||||
|
* to add the items that corresponds to this attribute to the constant pool of this class.
|
||||||
|
* @param code the bytecode of the method corresponding to this code attribute, or {@literal null}
|
||||||
|
* if this attribute is not a code attribute. Corresponds to the 'code' field of the Code
|
||||||
|
* attribute.
|
||||||
|
* @param codeLength the length of the bytecode of the method corresponding to this code
|
||||||
|
* attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length'
|
||||||
|
* field of the Code attribute.
|
||||||
|
* @param maxStack the maximum stack size of the method corresponding to this code attribute, or
|
||||||
|
* -1 if this attribute is not a code attribute.
|
||||||
|
* @param maxLocals the maximum number of local variables of the method corresponding to this code
|
||||||
|
* attribute, or -1 if this attribute is not a code attribute.
|
||||||
* @return the byte array form of this attribute.
|
* @return the byte array form of this attribute.
|
||||||
*/
|
*/
|
||||||
protected ByteVector write(final ClassWriter cw, final byte[] code,
|
protected ByteVector write(
|
||||||
final int len, final int maxStack, final int maxLocals) {
|
final ClassWriter classWriter,
|
||||||
ByteVector v = new ByteVector();
|
final byte[] code,
|
||||||
v.data = value;
|
final int codeLength,
|
||||||
v.length = value.length;
|
final int maxStack,
|
||||||
return v;
|
final int maxLocals) {
|
||||||
|
return new ByteVector(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the length of the attribute list that begins with this attribute.
|
* Returns the number of attributes of the attribute list that begins with this attribute.
|
||||||
*
|
*
|
||||||
* @return the length of the attribute list that begins with this attribute.
|
* @return the number of attributes of the attribute list that begins with this attribute.
|
||||||
*/
|
*/
|
||||||
final int getCount() {
|
final int getAttributeCount() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Attribute attr = this;
|
Attribute attribute = this;
|
||||||
while (attr != null) {
|
while (attribute != null) {
|
||||||
count += 1;
|
count += 1;
|
||||||
attr = attr.next;
|
attribute = attribute.nextAttribute;
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of all the attributes in this attribute list.
|
* Returns the total size in bytes of all the attributes in the attribute list that begins with
|
||||||
|
* this attribute. This size includes the 6 header bytes (attribute_name_index and
|
||||||
|
* attribute_length) per attribute. Also adds the attribute type names to the constant pool.
|
||||||
*
|
*
|
||||||
* @param cw
|
* @param symbolTable where the constants used in the attributes must be stored.
|
||||||
* the class writer to be used to convert the attributes into
|
* @return the size of all the attributes in this attribute list. This size includes the size of
|
||||||
* byte arrays, with the {@link #write write} method.
|
* the attribute headers.
|
||||||
* @param code
|
|
||||||
* the bytecode of the method corresponding to these code
|
|
||||||
* attributes, or <tt>null</tt> if these attributes are not code
|
|
||||||
* attributes.
|
|
||||||
* @param len
|
|
||||||
* the length of the bytecode of the method corresponding to
|
|
||||||
* these code attributes, or <tt>null</tt> if these attributes
|
|
||||||
* are not code attributes.
|
|
||||||
* @param maxStack
|
|
||||||
* the maximum stack size of the method corresponding to these
|
|
||||||
* code attributes, or -1 if these attributes are not code
|
|
||||||
* attributes.
|
|
||||||
* @param maxLocals
|
|
||||||
* the maximum number of local variables of the method
|
|
||||||
* corresponding to these code attributes, or -1 if these
|
|
||||||
* attributes are not code attributes.
|
|
||||||
* @return the size of all the attributes in this attribute list. This size
|
|
||||||
* includes the size of the attribute headers.
|
|
||||||
*/
|
*/
|
||||||
final int getSize(final ClassWriter cw, final byte[] code, final int len,
|
final int computeAttributesSize(final SymbolTable symbolTable) {
|
||||||
final int maxStack, final int maxLocals) {
|
final byte[] code = null;
|
||||||
Attribute attr = this;
|
final int codeLength = 0;
|
||||||
|
final int maxStack = -1;
|
||||||
|
final int maxLocals = -1;
|
||||||
|
return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total size in bytes of all the attributes in the attribute list that begins with
|
||||||
|
* this attribute. This size includes the 6 header bytes (attribute_name_index and
|
||||||
|
* attribute_length) per attribute. Also adds the attribute type names to the constant pool.
|
||||||
|
*
|
||||||
|
* @param symbolTable where the constants used in the attributes must be stored.
|
||||||
|
* @param code the bytecode of the method corresponding to these code attributes, or {@literal
|
||||||
|
* null} if they are not code attributes. Corresponds to the 'code' field of the Code
|
||||||
|
* attribute.
|
||||||
|
* @param codeLength the length of the bytecode of the method corresponding to these code
|
||||||
|
* attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
|
||||||
|
* the Code attribute.
|
||||||
|
* @param maxStack the maximum stack size of the method corresponding to these code attributes, or
|
||||||
|
* -1 if they are not code attributes.
|
||||||
|
* @param maxLocals the maximum number of local variables of the method corresponding to these
|
||||||
|
* code attributes, or -1 if they are not code attribute.
|
||||||
|
* @return the size of all the attributes in this attribute list. This size includes the size of
|
||||||
|
* the attribute headers.
|
||||||
|
*/
|
||||||
|
final int computeAttributesSize(
|
||||||
|
final SymbolTable symbolTable,
|
||||||
|
final byte[] code,
|
||||||
|
final int codeLength,
|
||||||
|
final int maxStack,
|
||||||
|
final int maxLocals) {
|
||||||
|
final ClassWriter classWriter = symbolTable.classWriter;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
while (attr != null) {
|
Attribute attribute = this;
|
||||||
cw.newUTF8(attr.type);
|
while (attribute != null) {
|
||||||
size += attr.write(cw, code, len, maxStack, maxLocals).length + 6;
|
symbolTable.addConstantUtf8(attribute.type);
|
||||||
attr = attr.next;
|
size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length;
|
||||||
|
attribute = attribute.nextAttribute;
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes all the attributes of this attribute list in the given byte
|
* Puts all the attributes of the attribute list that begins with this attribute, in the given
|
||||||
* vector.
|
* byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
|
||||||
|
* attribute.
|
||||||
*
|
*
|
||||||
* @param cw
|
* @param symbolTable where the constants used in the attributes must be stored.
|
||||||
* the class writer to be used to convert the attributes into
|
* @param output where the attributes must be written.
|
||||||
* byte arrays, with the {@link #write write} method.
|
|
||||||
* @param code
|
|
||||||
* the bytecode of the method corresponding to these code
|
|
||||||
* attributes, or <tt>null</tt> if these attributes are not code
|
|
||||||
* attributes.
|
|
||||||
* @param len
|
|
||||||
* the length of the bytecode of the method corresponding to
|
|
||||||
* these code attributes, or <tt>null</tt> if these attributes
|
|
||||||
* are not code attributes.
|
|
||||||
* @param maxStack
|
|
||||||
* the maximum stack size of the method corresponding to these
|
|
||||||
* code attributes, or -1 if these attributes are not code
|
|
||||||
* attributes.
|
|
||||||
* @param maxLocals
|
|
||||||
* the maximum number of local variables of the method
|
|
||||||
* corresponding to these code attributes, or -1 if these
|
|
||||||
* attributes are not code attributes.
|
|
||||||
* @param out
|
|
||||||
* where the attributes must be written.
|
|
||||||
*/
|
*/
|
||||||
final void put(final ClassWriter cw, final byte[] code, final int len,
|
final void putAttributes(final SymbolTable symbolTable, final ByteVector output) {
|
||||||
final int maxStack, final int maxLocals, final ByteVector out) {
|
final byte[] code = null;
|
||||||
Attribute attr = this;
|
final int codeLength = 0;
|
||||||
while (attr != null) {
|
final int maxStack = -1;
|
||||||
ByteVector b = attr.write(cw, code, len, maxStack, maxLocals);
|
final int maxLocals = -1;
|
||||||
out.putShort(cw.newUTF8(attr.type)).putInt(b.length);
|
putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output);
|
||||||
out.putByteArray(b.data, 0, b.length);
|
}
|
||||||
attr = attr.next;
|
|
||||||
|
/**
|
||||||
|
* Puts all the attributes of the attribute list that begins with this attribute, in the given
|
||||||
|
* byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per
|
||||||
|
* attribute.
|
||||||
|
*
|
||||||
|
* @param symbolTable where the constants used in the attributes must be stored.
|
||||||
|
* @param code the bytecode of the method corresponding to these code attributes, or {@literal
|
||||||
|
* null} if they are not code attributes. Corresponds to the 'code' field of the Code
|
||||||
|
* attribute.
|
||||||
|
* @param codeLength the length of the bytecode of the method corresponding to these code
|
||||||
|
* attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of
|
||||||
|
* the Code attribute.
|
||||||
|
* @param maxStack the maximum stack size of the method corresponding to these code attributes, or
|
||||||
|
* -1 if they are not code attributes.
|
||||||
|
* @param maxLocals the maximum number of local variables of the method corresponding to these
|
||||||
|
* code attributes, or -1 if they are not code attribute.
|
||||||
|
* @param output where the attributes must be written.
|
||||||
|
*/
|
||||||
|
final void putAttributes(
|
||||||
|
final SymbolTable symbolTable,
|
||||||
|
final byte[] code,
|
||||||
|
final int codeLength,
|
||||||
|
final int maxStack,
|
||||||
|
final int maxLocals,
|
||||||
|
final ByteVector output) {
|
||||||
|
final ClassWriter classWriter = symbolTable.classWriter;
|
||||||
|
Attribute attribute = this;
|
||||||
|
while (attribute != null) {
|
||||||
|
ByteVector attributeContent =
|
||||||
|
attribute.write(classWriter, code, codeLength, maxStack, maxLocals);
|
||||||
|
// Put attribute_name_index and attribute_length.
|
||||||
|
output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length);
|
||||||
|
output.putByteArray(attributeContent.data, 0, attributeContent.length);
|
||||||
|
attribute = attribute.nextAttribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed.
|
/** A set of attribute prototypes (attributes with the same type are considered equal). */
|
||||||
//see also changes in ClassReader.accept.
|
static final class Set {
|
||||||
|
|
||||||
public static class NestMembers extends Attribute {
|
private static final int SIZE_INCREMENT = 6;
|
||||||
public NestMembers() {
|
|
||||||
super("NestMembers");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes;
|
private int size;
|
||||||
String[] classes;
|
private Attribute[] data = new Attribute[SIZE_INCREMENT];
|
||||||
|
|
||||||
@Override
|
void addAttributes(final Attribute attributeList) {
|
||||||
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
|
Attribute attribute = attributeList;
|
||||||
int offset = off;
|
while (attribute != null) {
|
||||||
NestMembers a = new NestMembers();
|
if (!contains(attribute)) {
|
||||||
int size = cr.readShort(off);
|
add(attribute);
|
||||||
a.classes = new String[size];
|
|
||||||
off += 2;
|
|
||||||
for (int i = 0; i < size ; i++) {
|
|
||||||
a.classes[i] = cr.readClass(off, buf);
|
|
||||||
off += 2;
|
|
||||||
}
|
}
|
||||||
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
|
attribute = attribute.nextAttribute;
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
|
|
||||||
ByteVector v = new ByteVector(bytes.length);
|
|
||||||
v.putShort(classes.length);
|
|
||||||
for (String s : classes) {
|
|
||||||
v.putShort(cw.newClass(s));
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NestHost extends Attribute {
|
Attribute[] toArray() {
|
||||||
|
Attribute[] result = new Attribute[size];
|
||||||
byte[] bytes;
|
System.arraycopy(data, 0, result, 0, size);
|
||||||
String clazz;
|
return result;
|
||||||
|
|
||||||
public NestHost() {
|
|
||||||
super("NestHost");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean contains(final Attribute attribute) {
|
||||||
protected Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
|
for (int i = 0; i < size; ++i) {
|
||||||
int offset = off;
|
if (data[i].type.equals(attribute.type)) {
|
||||||
NestHost a = new NestHost();
|
return true;
|
||||||
a.clazz = cr.readClass(off, buf);
|
}
|
||||||
a.bytes = Arrays.copyOfRange(cr.b, offset, offset + len);
|
}
|
||||||
return a;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void add(final Attribute attribute) {
|
||||||
protected ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
|
if (size >= data.length) {
|
||||||
ByteVector v = new ByteVector(bytes.length);
|
Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
|
||||||
v.putShort(cw.newClass(clazz));
|
System.arraycopy(data, 0, newData, 0, size);
|
||||||
return v;
|
data = newData;
|
||||||
|
}
|
||||||
|
data[size++] = attribute;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final Attribute[] DEFAULT_ATTRIBUTE_PROTOS = new Attribute[] {
|
|
||||||
new NestMembers(),
|
|
||||||
new NestHost()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -59,309 +59,333 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dynamically extensible vector of bytes. This class is roughly equivalent to
|
* A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream
|
||||||
* a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
|
* on top of a ByteArrayOutputStream, but is more efficient.
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
public class ByteVector {
|
public class ByteVector {
|
||||||
|
|
||||||
/**
|
/** The content of this vector. Only the first {@link #length} bytes contain real data. */
|
||||||
* The content of this vector.
|
|
||||||
*/
|
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|
||||||
/**
|
/** The actual number of bytes in this vector. */
|
||||||
* Actual number of bytes in this vector.
|
|
||||||
*/
|
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
/**
|
/** Constructs a new {@link ByteVector} with a default initial capacity. */
|
||||||
* Constructs a new {@link ByteVector ByteVector} with a default initial
|
|
||||||
* size.
|
|
||||||
*/
|
|
||||||
public ByteVector() {
|
public ByteVector() {
|
||||||
data = new byte[64];
|
data = new byte[64];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link ByteVector ByteVector} with the given initial
|
* Constructs a new {@link ByteVector} with the given initial capacity.
|
||||||
* size.
|
|
||||||
*
|
*
|
||||||
* @param initialSize
|
* @param initialCapacity the initial capacity of the byte vector to be constructed.
|
||||||
* the initial size of the byte vector to be constructed.
|
|
||||||
*/
|
*/
|
||||||
public ByteVector(final int initialSize) {
|
public ByteVector(final int initialCapacity) {
|
||||||
data = new byte[initialSize];
|
data = new byte[initialCapacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a byte into this byte vector. The byte vector is automatically
|
* Constructs a new {@link ByteVector} from the given initial data.
|
||||||
* enlarged if necessary.
|
|
||||||
*
|
*
|
||||||
* @param b
|
* @param data the initial data of the new byte vector.
|
||||||
* a byte.
|
*/
|
||||||
|
ByteVector(final byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
this.length = data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
|
||||||
|
*
|
||||||
|
* @param byteValue a byte.
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putByte(final int b) {
|
public ByteVector putByte(final int byteValue) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 1 > data.length) {
|
if (currentLength + 1 > data.length) {
|
||||||
enlarge(1);
|
enlarge(1);
|
||||||
}
|
}
|
||||||
data[length++] = (byte) b;
|
data[currentLength++] = (byte) byteValue;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts two bytes into this byte vector. The byte vector is automatically
|
* Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary.
|
||||||
* enlarged if necessary.
|
|
||||||
*
|
*
|
||||||
* @param b1
|
* @param byteValue1 a byte.
|
||||||
* a byte.
|
* @param byteValue2 another byte.
|
||||||
* @param b2
|
|
||||||
* another byte.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
ByteVector put11(final int b1, final int b2) {
|
final ByteVector put11(final int byteValue1, final int byteValue2) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 2 > data.length) {
|
if (currentLength + 2 > data.length) {
|
||||||
enlarge(2);
|
enlarge(2);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
data[length++] = (byte) b1;
|
currentData[currentLength++] = (byte) byteValue1;
|
||||||
data[length++] = (byte) b2;
|
currentData[currentLength++] = (byte) byteValue2;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a short into this byte vector. The byte vector is automatically
|
* Puts a short into this byte vector. The byte vector is automatically enlarged if necessary.
|
||||||
* enlarged if necessary.
|
|
||||||
*
|
*
|
||||||
* @param s
|
* @param shortValue a short.
|
||||||
* a short.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putShort(final int s) {
|
public ByteVector putShort(final int shortValue) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 2 > data.length) {
|
if (currentLength + 2 > data.length) {
|
||||||
enlarge(2);
|
enlarge(2);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
data[length++] = (byte) (s >>> 8);
|
currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||||||
data[length++] = (byte) s;
|
currentData[currentLength++] = (byte) shortValue;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a byte and a short into this byte vector. The byte vector is
|
* Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if
|
||||||
* automatically enlarged if necessary.
|
* necessary.
|
||||||
*
|
*
|
||||||
* @param b
|
* @param byteValue a byte.
|
||||||
* a byte.
|
* @param shortValue a short.
|
||||||
* @param s
|
|
||||||
* a short.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
ByteVector put12(final int b, final int s) {
|
final ByteVector put12(final int byteValue, final int shortValue) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 3 > data.length) {
|
if (currentLength + 3 > data.length) {
|
||||||
enlarge(3);
|
enlarge(3);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
data[length++] = (byte) b;
|
currentData[currentLength++] = (byte) byteValue;
|
||||||
data[length++] = (byte) (s >>> 8);
|
currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||||||
data[length++] = (byte) s;
|
currentData[currentLength++] = (byte) shortValue;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts an int into this byte vector. The byte vector is automatically
|
* Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if
|
||||||
* enlarged if necessary.
|
* necessary.
|
||||||
*
|
*
|
||||||
* @param i
|
* @param byteValue1 a byte.
|
||||||
* an int.
|
* @param byteValue2 another byte.
|
||||||
|
* @param shortValue a short.
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putInt(final int i) {
|
final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 4 > data.length) {
|
if (currentLength + 4 > data.length) {
|
||||||
enlarge(4);
|
enlarge(4);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
data[length++] = (byte) (i >>> 24);
|
currentData[currentLength++] = (byte) byteValue1;
|
||||||
data[length++] = (byte) (i >>> 16);
|
currentData[currentLength++] = (byte) byteValue2;
|
||||||
data[length++] = (byte) (i >>> 8);
|
currentData[currentLength++] = (byte) (shortValue >>> 8);
|
||||||
data[length++] = (byte) i;
|
currentData[currentLength++] = (byte) shortValue;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a long into this byte vector. The byte vector is automatically
|
* Puts an int into this byte vector. The byte vector is automatically enlarged if necessary.
|
||||||
* enlarged if necessary.
|
|
||||||
*
|
*
|
||||||
* @param l
|
* @param intValue an int.
|
||||||
* a long.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putLong(final long l) {
|
public ByteVector putInt(final int intValue) {
|
||||||
int length = this.length;
|
int currentLength = length;
|
||||||
if (length + 8 > data.length) {
|
if (currentLength + 4 > data.length) {
|
||||||
|
enlarge(4);
|
||||||
|
}
|
||||||
|
byte[] currentData = data;
|
||||||
|
currentData[currentLength++] = (byte) (intValue >>> 24);
|
||||||
|
currentData[currentLength++] = (byte) (intValue >>> 16);
|
||||||
|
currentData[currentLength++] = (byte) (intValue >>> 8);
|
||||||
|
currentData[currentLength++] = (byte) intValue;
|
||||||
|
length = currentLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged
|
||||||
|
* if necessary.
|
||||||
|
*
|
||||||
|
* @param byteValue a byte.
|
||||||
|
* @param shortValue1 a short.
|
||||||
|
* @param shortValue2 another short.
|
||||||
|
* @return this byte vector.
|
||||||
|
*/
|
||||||
|
final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) {
|
||||||
|
int currentLength = length;
|
||||||
|
if (currentLength + 5 > data.length) {
|
||||||
|
enlarge(5);
|
||||||
|
}
|
||||||
|
byte[] currentData = data;
|
||||||
|
currentData[currentLength++] = (byte) byteValue;
|
||||||
|
currentData[currentLength++] = (byte) (shortValue1 >>> 8);
|
||||||
|
currentData[currentLength++] = (byte) shortValue1;
|
||||||
|
currentData[currentLength++] = (byte) (shortValue2 >>> 8);
|
||||||
|
currentData[currentLength++] = (byte) shortValue2;
|
||||||
|
length = currentLength;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a long into this byte vector. The byte vector is automatically enlarged if necessary.
|
||||||
|
*
|
||||||
|
* @param longValue a long.
|
||||||
|
* @return this byte vector.
|
||||||
|
*/
|
||||||
|
public ByteVector putLong(final long longValue) {
|
||||||
|
int currentLength = length;
|
||||||
|
if (currentLength + 8 > data.length) {
|
||||||
enlarge(8);
|
enlarge(8);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
int i = (int) (l >>> 32);
|
int intValue = (int) (longValue >>> 32);
|
||||||
data[length++] = (byte) (i >>> 24);
|
currentData[currentLength++] = (byte) (intValue >>> 24);
|
||||||
data[length++] = (byte) (i >>> 16);
|
currentData[currentLength++] = (byte) (intValue >>> 16);
|
||||||
data[length++] = (byte) (i >>> 8);
|
currentData[currentLength++] = (byte) (intValue >>> 8);
|
||||||
data[length++] = (byte) i;
|
currentData[currentLength++] = (byte) intValue;
|
||||||
i = (int) l;
|
intValue = (int) longValue;
|
||||||
data[length++] = (byte) (i >>> 24);
|
currentData[currentLength++] = (byte) (intValue >>> 24);
|
||||||
data[length++] = (byte) (i >>> 16);
|
currentData[currentLength++] = (byte) (intValue >>> 16);
|
||||||
data[length++] = (byte) (i >>> 8);
|
currentData[currentLength++] = (byte) (intValue >>> 8);
|
||||||
data[length++] = (byte) i;
|
currentData[currentLength++] = (byte) intValue;
|
||||||
this.length = length;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts an UTF8 string into this byte vector. The byte vector is
|
* Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
|
||||||
* automatically enlarged if necessary.
|
* necessary.
|
||||||
*
|
*
|
||||||
* @param s
|
* @param stringValue a String whose UTF8 encoded length must be less than 65536.
|
||||||
* a String whose UTF8 encoded length must be less than 65536.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putUTF8(final String s) {
|
// DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility).
|
||||||
int charLength = s.length();
|
public ByteVector putUTF8(final String stringValue) {
|
||||||
|
int charLength = stringValue.length();
|
||||||
if (charLength > 65535) {
|
if (charLength > 65535) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException("UTF8 string too large");
|
||||||
}
|
}
|
||||||
int len = length;
|
int currentLength = length;
|
||||||
if (len + 2 + charLength > data.length) {
|
if (currentLength + 2 + charLength > data.length) {
|
||||||
enlarge(2 + charLength);
|
enlarge(2 + charLength);
|
||||||
}
|
}
|
||||||
byte[] data = this.data;
|
byte[] currentData = data;
|
||||||
// optimistic algorithm: instead of computing the byte length and then
|
// Optimistic algorithm: instead of computing the byte length and then serializing the string
|
||||||
// serializing the string (which requires two loops), we assume the byte
|
// (which requires two loops), we assume the byte length is equal to char length (which is the
|
||||||
// length is equal to char length (which is the most frequent case), and
|
// most frequent case), and we start serializing the string right away. During the
|
||||||
// we start serializing the string right away. During the serialization,
|
// serialization, if we find that this assumption is wrong, we continue with the general method.
|
||||||
// if we find that this assumption is wrong, we continue with the
|
currentData[currentLength++] = (byte) (charLength >>> 8);
|
||||||
// general method.
|
currentData[currentLength++] = (byte) charLength;
|
||||||
data[len++] = (byte) (charLength >>> 8);
|
|
||||||
data[len++] = (byte) charLength;
|
|
||||||
for (int i = 0; i < charLength; ++i) {
|
for (int i = 0; i < charLength; ++i) {
|
||||||
char c = s.charAt(i);
|
char charValue = stringValue.charAt(i);
|
||||||
if (c >= '\001' && c <= '\177') {
|
if (charValue >= '\u0001' && charValue <= '\u007F') {
|
||||||
data[len++] = (byte) c;
|
currentData[currentLength++] = (byte) charValue;
|
||||||
} else {
|
} else {
|
||||||
length = len;
|
length = currentLength;
|
||||||
return encodeUTF8(s, i, 65535);
|
return encodeUtf8(stringValue, i, 65535);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
length = len;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts an UTF8 string into this byte vector. The byte vector is
|
* Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if
|
||||||
* automatically enlarged if necessary. The string length is encoded in two
|
* necessary. The string length is encoded in two bytes before the encoded characters, if there is
|
||||||
* bytes before the encoded characters, if there is space for that (i.e. if
|
* space for that (i.e. if this.length - offset - 2 >= 0).
|
||||||
* this.length - i - 2 >= 0).
|
|
||||||
*
|
*
|
||||||
* @param s
|
* @param stringValue the String to encode.
|
||||||
* the String to encode.
|
* @param offset the index of the first character to encode. The previous characters are supposed
|
||||||
* @param i
|
* to have already been encoded, using only one byte per character.
|
||||||
* the index of the first character to encode. The previous
|
* @param maxByteLength the maximum byte length of the encoded string, including the already
|
||||||
* characters are supposed to have already been encoded, using
|
* encoded characters.
|
||||||
* only one byte per character.
|
|
||||||
* @param maxByteLength
|
|
||||||
* the maximum byte length of the encoded string, including the
|
|
||||||
* already encoded characters.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
ByteVector encodeUTF8(final String s, int i, int maxByteLength) {
|
final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) {
|
||||||
int charLength = s.length();
|
int charLength = stringValue.length();
|
||||||
int byteLength = i;
|
int byteLength = offset;
|
||||||
char c;
|
for (int i = offset; i < charLength; ++i) {
|
||||||
for (int j = i; j < charLength; ++j) {
|
char charValue = stringValue.charAt(i);
|
||||||
c = s.charAt(j);
|
if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||||||
if (c >= '\001' && c <= '\177') {
|
|
||||||
byteLength++;
|
byteLength++;
|
||||||
} else if (c > '\u07FF') {
|
} else if (charValue <= 0x07FF) {
|
||||||
byteLength += 3;
|
|
||||||
} else {
|
|
||||||
byteLength += 2;
|
byteLength += 2;
|
||||||
|
} else {
|
||||||
|
byteLength += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (byteLength > maxByteLength) {
|
if (byteLength > maxByteLength) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException("UTF8 string too large");
|
||||||
}
|
}
|
||||||
int start = length - i - 2;
|
// Compute where 'byteLength' must be stored in 'data', and store it at this location.
|
||||||
if (start >= 0) {
|
int byteLengthOffset = length - offset - 2;
|
||||||
data[start] = (byte) (byteLength >>> 8);
|
if (byteLengthOffset >= 0) {
|
||||||
data[start + 1] = (byte) byteLength;
|
data[byteLengthOffset] = (byte) (byteLength >>> 8);
|
||||||
|
data[byteLengthOffset + 1] = (byte) byteLength;
|
||||||
}
|
}
|
||||||
if (length + byteLength - i > data.length) {
|
if (length + byteLength - offset > data.length) {
|
||||||
enlarge(byteLength - i);
|
enlarge(byteLength - offset);
|
||||||
}
|
}
|
||||||
int len = length;
|
int currentLength = length;
|
||||||
for (int j = i; j < charLength; ++j) {
|
for (int i = offset; i < charLength; ++i) {
|
||||||
c = s.charAt(j);
|
char charValue = stringValue.charAt(i);
|
||||||
if (c >= '\001' && c <= '\177') {
|
if (charValue >= 0x0001 && charValue <= 0x007F) {
|
||||||
data[len++] = (byte) c;
|
data[currentLength++] = (byte) charValue;
|
||||||
} else if (c > '\u07FF') {
|
} else if (charValue <= 0x07FF) {
|
||||||
data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
|
data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F);
|
||||||
data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
|
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
||||||
data[len++] = (byte) (0x80 | c & 0x3F);
|
|
||||||
} else {
|
} else {
|
||||||
data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
|
data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF);
|
||||||
data[len++] = (byte) (0x80 | c & 0x3F);
|
data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F);
|
||||||
|
data[currentLength++] = (byte) (0x80 | charValue & 0x3F);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
length = len;
|
length = currentLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts an array of bytes into this byte vector. The byte vector is
|
* Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if
|
||||||
* automatically enlarged if necessary.
|
* necessary.
|
||||||
*
|
*
|
||||||
* @param b
|
* @param byteArrayValue an array of bytes. May be {@literal null} to put {@code byteLength} null
|
||||||
* an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
|
* bytes into this byte vector.
|
||||||
* null bytes into this byte vector.
|
* @param byteOffset index of the first byte of byteArrayValue that must be copied.
|
||||||
* @param off
|
* @param byteLength number of bytes of byteArrayValue that must be copied.
|
||||||
* index of the fist byte of b that must be copied.
|
|
||||||
* @param len
|
|
||||||
* number of bytes of b that must be copied.
|
|
||||||
* @return this byte vector.
|
* @return this byte vector.
|
||||||
*/
|
*/
|
||||||
public ByteVector putByteArray(final byte[] b, final int off, final int len) {
|
public ByteVector putByteArray(
|
||||||
if (length + len > data.length) {
|
final byte[] byteArrayValue, final int byteOffset, final int byteLength) {
|
||||||
enlarge(len);
|
if (length + byteLength > data.length) {
|
||||||
|
enlarge(byteLength);
|
||||||
}
|
}
|
||||||
if (b != null) {
|
if (byteArrayValue != null) {
|
||||||
System.arraycopy(b, off, data, length, len);
|
System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength);
|
||||||
}
|
}
|
||||||
length += len;
|
length += byteLength;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enlarge this byte vector so that it can receive n more bytes.
|
* Enlarges this byte vector so that it can receive 'size' more bytes.
|
||||||
*
|
*
|
||||||
* @param size
|
* @param size number of additional bytes that this byte vector should be able to receive.
|
||||||
* number of additional bytes that this byte vector should be
|
|
||||||
* able to receive.
|
|
||||||
*/
|
*/
|
||||||
private void enlarge(final int size) {
|
private void enlarge(final int size) {
|
||||||
int length1 = 2 * data.length;
|
int doubleCapacity = 2 * data.length;
|
||||||
int length2 = length + size;
|
int minimalCapacity = length + size;
|
||||||
byte[] newData = new byte[length1 > length2 ? length1 : length2];
|
byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity];
|
||||||
System.arraycopy(data, 0, newData, 0, length);
|
System.arraycopy(data, 0, newData, 0, length);
|
||||||
data = newData;
|
data = newData;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file:
|
||||||
|
*
|
||||||
|
* ASM: a very small and fast Java bytecode manipulation framework
|
||||||
|
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when the constant pool of a class produced by a {@link ClassWriter} is too
|
||||||
|
* large.
|
||||||
|
*
|
||||||
|
* @author Jason Zaugg
|
||||||
|
*/
|
||||||
|
public final class ClassTooLargeException extends IndexOutOfBoundsException {
|
||||||
|
private static final long serialVersionUID = 160715609518896765L;
|
||||||
|
|
||||||
|
private final String className;
|
||||||
|
private final int constantPoolCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@link ClassTooLargeException}.
|
||||||
|
*
|
||||||
|
* @param className the internal name of the class.
|
||||||
|
* @param constantPoolCount the number of constant pool items of the class.
|
||||||
|
*/
|
||||||
|
public ClassTooLargeException(final String className, final int constantPoolCount) {
|
||||||
|
super("Class too large: " + className);
|
||||||
|
this.className = className;
|
||||||
|
this.constantPoolCount = constantPoolCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the internal name of the class.
|
||||||
|
*
|
||||||
|
* @return the internal name of the class.
|
||||||
|
*/
|
||||||
|
public String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of constant pool items of the class.
|
||||||
|
*
|
||||||
|
* @return the number of constant pool items of the class.
|
||||||
|
*/
|
||||||
|
public int getConstantPoolCount() {
|
||||||
|
return constantPoolCount;
|
||||||
|
}
|
||||||
|
}
|
@ -59,35 +59,30 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visitor to visit a Java class. The methods of this class must be called in
|
* A visitor to visit a Java class. The methods of this class must be called in the following order:
|
||||||
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
|
* {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code
|
||||||
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
|
* visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code
|
||||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
|
* visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} |
|
||||||
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
|
* {@code visitMethod} )* {@code visitEnd}.
|
||||||
* <tt>visitEnd</tt>.
|
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
public abstract class ClassVisitor {
|
public abstract class ClassVisitor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ASM API version implemented by this visitor. The value of this field
|
* The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
||||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
*/
|
*/
|
||||||
protected final int api;
|
protected final int api;
|
||||||
|
|
||||||
/**
|
/** The class visitor to which this visitor must delegate method calls. May be null. */
|
||||||
* The class visitor to which this visitor must delegate method calls. May
|
|
||||||
* be null.
|
|
||||||
*/
|
|
||||||
protected ClassVisitor cv;
|
protected ClassVisitor cv;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link ClassVisitor}.
|
* Constructs a new {@link ClassVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
|
||||||
*/
|
*/
|
||||||
public ClassVisitor(final int api) {
|
public ClassVisitor(final int api) {
|
||||||
this(api, null);
|
this(api, null);
|
||||||
@ -96,48 +91,42 @@ public abstract class ClassVisitor {
|
|||||||
/**
|
/**
|
||||||
* Constructs a new {@link ClassVisitor}.
|
* Constructs a new {@link ClassVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* @param classVisitor the class visitor to which this visitor must delegate method calls. May be
|
||||||
* @param cv
|
* null.
|
||||||
* the class visitor to which this visitor must delegate method
|
|
||||||
* calls. May be null.
|
|
||||||
*/
|
*/
|
||||||
public ClassVisitor(final int api, final ClassVisitor cv) {
|
public ClassVisitor(final int api, final ClassVisitor classVisitor) {
|
||||||
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
|
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.cv = cv;
|
this.cv = classVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits the header of the class.
|
* Visits the header of the class.
|
||||||
*
|
*
|
||||||
* @param version
|
* @param version the class version. The minor version is stored in the 16 most significant bits,
|
||||||
* the class version.
|
* and the major version in the 16 least significant bits.
|
||||||
* @param access
|
* @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if
|
||||||
* the class's access flags (see {@link Opcodes}). This parameter
|
* the class is deprecated.
|
||||||
* also indicates if the class is deprecated.
|
* @param name the internal name of the class (see {@link Type#getInternalName()}).
|
||||||
* @param name
|
* @param signature the signature of this class. May be {@literal null} if the class is not a
|
||||||
* the internal name of the class (see
|
* generic one, and does not extend or implement generic classes or interfaces.
|
||||||
* {@link Type#getInternalName() getInternalName}).
|
* @param superName the internal of name of the super class (see {@link Type#getInternalName()}).
|
||||||
* @param signature
|
* For interfaces, the super class is {@link Object}. May be {@literal null}, but only for the
|
||||||
* the signature of this class. May be <tt>null</tt> if the class
|
* {@link Object} class.
|
||||||
* is not a generic one, and does not extend or implement generic
|
* @param interfaces the internal names of the class's interfaces (see {@link
|
||||||
* classes or interfaces.
|
* Type#getInternalName()}). May be {@literal null}.
|
||||||
* @param superName
|
|
||||||
* the internal of name of the super class (see
|
|
||||||
* {@link Type#getInternalName() getInternalName}). For
|
|
||||||
* interfaces, the super class is {@link Object}. May be
|
|
||||||
* <tt>null</tt>, but only for the {@link Object} class.
|
|
||||||
* @param interfaces
|
|
||||||
* the internal names of the class's interfaces (see
|
|
||||||
* {@link Type#getInternalName() getInternalName}). May be
|
|
||||||
* <tt>null</tt>.
|
|
||||||
*/
|
*/
|
||||||
public void visit(int version, int access, String name, String signature,
|
public void visit(
|
||||||
String superName, String[] interfaces) {
|
final int version,
|
||||||
|
final int access,
|
||||||
|
final String name,
|
||||||
|
final String signature,
|
||||||
|
final String superName,
|
||||||
|
final String[] interfaces) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
cv.visit(version, access, name, signature, superName, interfaces);
|
cv.visit(version, access, name, signature, superName, interfaces);
|
||||||
}
|
}
|
||||||
@ -146,15 +135,12 @@ public abstract class ClassVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits the source of the class.
|
* Visits the source of the class.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source the name of the source file from which the class was compiled. May be {@literal
|
||||||
* the name of the source file from which the class was compiled.
|
* null}.
|
||||||
* May be <tt>null</tt>.
|
* @param debug additional debug information to compute the correspondence between source and
|
||||||
* @param debug
|
* compiled elements of the class. May be {@literal null}.
|
||||||
* additional debug information to compute the correspondance
|
|
||||||
* between source and compiled elements of the class. May be
|
|
||||||
* <tt>null</tt>.
|
|
||||||
*/
|
*/
|
||||||
public void visitSource(String source, String debug) {
|
public void visitSource(final String source, final String debug) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
cv.visitSource(source, debug);
|
cv.visitSource(source, debug);
|
||||||
}
|
}
|
||||||
@ -162,19 +148,17 @@ public abstract class ClassVisitor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit the module corresponding to the class.
|
* Visit the module corresponding to the class.
|
||||||
* @param name
|
*
|
||||||
* module name
|
* @param name the fully qualified name (using dots) of the module.
|
||||||
* @param access
|
* @param access the module access flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} and {@code
|
||||||
* module flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC}
|
* ACC_MANDATED}.
|
||||||
* and {@code ACC_MANDATED}.
|
* @param version the module version, or {@literal null}.
|
||||||
* @param version
|
* @return a visitor to visit the module values, or {@literal null} if this visitor is not
|
||||||
* module version or null.
|
* interested in visiting this module.
|
||||||
* @return a visitor to visit the module values, or <tt>null</tt> if
|
|
||||||
* this visitor is not interested in visiting this module.
|
|
||||||
*/
|
*/
|
||||||
public ModuleVisitor visitModule(String name, int access, String version) {
|
public ModuleVisitor visitModule(final String name, final int access, final String version) {
|
||||||
if (api < Opcodes.ASM6) {
|
if (api < Opcodes.ASM6) {
|
||||||
throw new RuntimeException();
|
throw new UnsupportedOperationException("This feature requires ASM6");
|
||||||
}
|
}
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
return cv.visitModule(name, access, version);
|
return cv.visitModule(name, access, version);
|
||||||
@ -183,39 +167,51 @@ public abstract class ClassVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits the enclosing class of the class. This method must be called only
|
* Visits the nest host class of the class. A nest is a set of classes of the same package that
|
||||||
* if the class has an enclosing class.
|
* share access to their private members. One of these classes, called the host, lists the other
|
||||||
|
* members of the nest, which in turn should link to the host of their nest. This method must be
|
||||||
|
* called only once and only if the visited class is a non-host member of a nest. A class is
|
||||||
|
* implicitly its own nest, so it's invalid to call this method with the visited class name as
|
||||||
|
* argument.
|
||||||
*
|
*
|
||||||
* @param owner
|
* @param nestHost the internal name of the host class of the nest.
|
||||||
* internal name of the enclosing class of the class.
|
|
||||||
* @param name
|
|
||||||
* the name of the method that contains the class, or
|
|
||||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
|
||||||
* enclosing class.
|
|
||||||
* @param desc
|
|
||||||
* the descriptor of the method that contains the class, or
|
|
||||||
* <tt>null</tt> if the class is not enclosed in a method of its
|
|
||||||
* enclosing class.
|
|
||||||
*/
|
*/
|
||||||
public void visitOuterClass(String owner, String name, String desc) {
|
public void visitNestHost(final String nestHost) {
|
||||||
|
if (api < Opcodes.ASM7) {
|
||||||
|
throw new UnsupportedOperationException("This feature requires ASM7");
|
||||||
|
}
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
cv.visitOuterClass(owner, name, desc);
|
cv.visitNestHost(nestHost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits the enclosing class of the class. This method must be called only if the class has an
|
||||||
|
* enclosing class.
|
||||||
|
*
|
||||||
|
* @param owner internal name of the enclosing class of the class.
|
||||||
|
* @param name the name of the method that contains the class, or {@literal null} if the class is
|
||||||
|
* not enclosed in a method of its enclosing class.
|
||||||
|
* @param descriptor the descriptor of the method that contains the class, or {@literal null} if
|
||||||
|
* the class is not enclosed in a method of its enclosing class.
|
||||||
|
*/
|
||||||
|
public void visitOuterClass(final String owner, final String name, final String descriptor) {
|
||||||
|
if (cv != null) {
|
||||||
|
cv.visitOuterClass(owner, name, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits an annotation of the class.
|
* Visits an annotation of the class.
|
||||||
*
|
*
|
||||||
* @param desc
|
* @param descriptor the class descriptor of the annotation class.
|
||||||
* the class descriptor of the annotation class.
|
* @param visible {@literal true} if the annotation is visible at runtime.
|
||||||
* @param visible
|
* @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||||||
* <tt>true</tt> if the annotation is visible at runtime.
|
* interested in visiting this annotation.
|
||||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
|
||||||
* this visitor is not interested in visiting this annotation.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
return cv.visitAnnotation(desc, visible);
|
return cv.visitAnnotation(descriptor, visible);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -223,32 +219,25 @@ public abstract class ClassVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits an annotation on a type in the class signature.
|
* Visits an annotation on a type in the class signature.
|
||||||
*
|
*
|
||||||
* @param typeRef
|
* @param typeRef a reference to the annotated type. The sort of this type reference must be
|
||||||
* a reference to the annotated type. The sort of this type
|
* {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link
|
||||||
* reference must be {@link TypeReference#CLASS_TYPE_PARAMETER
|
* TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See
|
||||||
* CLASS_TYPE_PARAMETER},
|
|
||||||
* {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND
|
|
||||||
* CLASS_TYPE_PARAMETER_BOUND} or
|
|
||||||
* {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See
|
|
||||||
* {@link TypeReference}.
|
* {@link TypeReference}.
|
||||||
* @param typePath
|
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
|
||||||
* the path to the annotated type argument, wildcard bound, array
|
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
|
||||||
* element type, or static inner type within 'typeRef'. May be
|
* 'typeRef' as a whole.
|
||||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
* @param descriptor the class descriptor of the annotation class.
|
||||||
* @param desc
|
* @param visible {@literal true} if the annotation is visible at runtime.
|
||||||
* the class descriptor of the annotation class.
|
* @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||||||
* @param visible
|
* interested in visiting this annotation.
|
||||||
* <tt>true</tt> if the annotation is visible at runtime.
|
|
||||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
|
||||||
* this visitor is not interested in visiting this annotation.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
TypePath typePath, String desc, boolean visible) {
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||||||
if (api < Opcodes.ASM5) {
|
if (api < Opcodes.ASM5) {
|
||||||
throw new RuntimeException();
|
throw new UnsupportedOperationException("This feature requires ASM5");
|
||||||
}
|
}
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
|
return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -256,35 +245,46 @@ public abstract class ClassVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits a non standard attribute of the class.
|
* Visits a non standard attribute of the class.
|
||||||
*
|
*
|
||||||
* @param attr
|
* @param attribute an attribute.
|
||||||
* an attribute.
|
|
||||||
*/
|
*/
|
||||||
public void visitAttribute(Attribute attr) {
|
public void visitAttribute(final Attribute attribute) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
cv.visitAttribute(attr);
|
cv.visitAttribute(attribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits information about an inner class. This inner class is not
|
* Visits a member of the nest. A nest is a set of classes of the same package that share access
|
||||||
* necessarily a member of the class being visited.
|
* to their private members. One of these classes, called the host, lists the other members of the
|
||||||
|
* nest, which in turn should link to the host of their nest. This method must be called only if
|
||||||
|
* the visited class is the host of a nest. A nest host is implicitly a member of its own nest, so
|
||||||
|
* it's invalid to call this method with the visited class name as argument.
|
||||||
*
|
*
|
||||||
* @param name
|
* @param nestMember the internal name of a nest member.
|
||||||
* the internal name of an inner class (see
|
|
||||||
* {@link Type#getInternalName() getInternalName}).
|
|
||||||
* @param outerName
|
|
||||||
* the internal name of the class to which the inner class
|
|
||||||
* belongs (see {@link Type#getInternalName() getInternalName}).
|
|
||||||
* May be <tt>null</tt> for not member classes.
|
|
||||||
* @param innerName
|
|
||||||
* the (simple) name of the inner class inside its enclosing
|
|
||||||
* class. May be <tt>null</tt> for anonymous inner classes.
|
|
||||||
* @param access
|
|
||||||
* the access flags of the inner class as originally declared in
|
|
||||||
* the enclosing class.
|
|
||||||
*/
|
*/
|
||||||
public void visitInnerClass(String name, String outerName,
|
public void visitNestMember(final String nestMember) {
|
||||||
String innerName, int access) {
|
if (api < Opcodes.ASM7) {
|
||||||
|
throw new UnsupportedOperationException("This feature requires ASM7");
|
||||||
|
}
|
||||||
|
if (cv != null) {
|
||||||
|
cv.visitNestMember(nestMember);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits information about an inner class. This inner class is not necessarily a member of the
|
||||||
|
* class being visited.
|
||||||
|
*
|
||||||
|
* @param name the internal name of an inner class (see {@link Type#getInternalName()}).
|
||||||
|
* @param outerName the internal name of the class to which the inner class belongs (see {@link
|
||||||
|
* Type#getInternalName()}). May be {@literal null} for not member classes.
|
||||||
|
* @param innerName the (simple) name of the inner class inside its enclosing class. May be
|
||||||
|
* {@literal null} for anonymous inner classes.
|
||||||
|
* @param access the access flags of the inner class as originally declared in the enclosing
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
public void visitInnerClass(
|
||||||
|
final String name, final String outerName, final String innerName, final int access) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
cv.visitInnerClass(name, outerName, innerName, access);
|
cv.visitInnerClass(name, outerName, innerName, access);
|
||||||
}
|
}
|
||||||
@ -293,75 +293,64 @@ public abstract class ClassVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits a field of the class.
|
* Visits a field of the class.
|
||||||
*
|
*
|
||||||
* @param access
|
* @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if
|
||||||
* the field's access flags (see {@link Opcodes}). This parameter
|
* the field is synthetic and/or deprecated.
|
||||||
* also indicates if the field is synthetic and/or deprecated.
|
* @param name the field's name.
|
||||||
* @param name
|
* @param descriptor the field's descriptor (see {@link Type}).
|
||||||
* the field's name.
|
* @param signature the field's signature. May be {@literal null} if the field's type does not use
|
||||||
* @param desc
|
* generic types.
|
||||||
* the field's descriptor (see {@link Type Type}).
|
* @param value the field's initial value. This parameter, which may be {@literal null} if the
|
||||||
* @param signature
|
* field does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link
|
||||||
* the field's signature. May be <tt>null</tt> if the field's
|
* Long}, a {@link Double} or a {@link String} (for {@code int}, {@code float}, {@code long}
|
||||||
* type does not use generic types.
|
* or {@code String} fields respectively). <i>This parameter is only used for static
|
||||||
* @param value
|
* fields</i>. Its value is ignored for non static fields, which must be initialized through
|
||||||
* the field's initial value. This parameter, which may be
|
* bytecode instructions in constructors or methods.
|
||||||
* <tt>null</tt> if the field does not have an initial value,
|
* @return a visitor to visit field annotations and attributes, or {@literal null} if this class
|
||||||
* must be an {@link Integer}, a {@link Float}, a {@link Long}, a
|
* visitor is not interested in visiting these annotations and attributes.
|
||||||
* {@link Double} or a {@link String} (for <tt>int</tt>,
|
|
||||||
* <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
|
|
||||||
* respectively). <i>This parameter is only used for static
|
|
||||||
* fields</i>. Its value is ignored for non static fields, which
|
|
||||||
* must be initialized through bytecode instructions in
|
|
||||||
* constructors or methods.
|
|
||||||
* @return a visitor to visit field annotations and attributes, or
|
|
||||||
* <tt>null</tt> if this class visitor is not interested in visiting
|
|
||||||
* these annotations and attributes.
|
|
||||||
*/
|
*/
|
||||||
public FieldVisitor visitField(int access, String name, String desc,
|
public FieldVisitor visitField(
|
||||||
String signature, Object value) {
|
final int access,
|
||||||
|
final String name,
|
||||||
|
final String descriptor,
|
||||||
|
final String signature,
|
||||||
|
final Object value) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
return cv.visitField(access, name, desc, signature, value);
|
return cv.visitField(access, name, descriptor, signature, value);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a method of the class. This method <i>must</i> return a new
|
* Visits a method of the class. This method <i>must</i> return a new {@link MethodVisitor}
|
||||||
* {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
|
* instance (or {@literal null}) each time it is called, i.e., it should not return a previously
|
||||||
* i.e., it should not return a previously returned visitor.
|
* returned visitor.
|
||||||
*
|
*
|
||||||
* @param access
|
* @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if
|
||||||
* the method's access flags (see {@link Opcodes}). This
|
* the method is synthetic and/or deprecated.
|
||||||
* parameter also indicates if the method is synthetic and/or
|
* @param name the method's name.
|
||||||
* deprecated.
|
* @param descriptor the method's descriptor (see {@link Type}).
|
||||||
* @param name
|
* @param signature the method's signature. May be {@literal null} if the method parameters,
|
||||||
* the method's name.
|
* return type and exceptions do not use generic types.
|
||||||
* @param desc
|
* @param exceptions the internal names of the method's exception classes (see {@link
|
||||||
* the method's descriptor (see {@link Type Type}).
|
* Type#getInternalName()}). May be {@literal null}.
|
||||||
* @param signature
|
* @return an object to visit the byte code of the method, or {@literal null} if this class
|
||||||
* the method's signature. May be <tt>null</tt> if the method
|
* visitor is not interested in visiting the code of this method.
|
||||||
* parameters, return type and exceptions do not use generic
|
|
||||||
* types.
|
|
||||||
* @param exceptions
|
|
||||||
* the internal names of the method's exception classes (see
|
|
||||||
* {@link Type#getInternalName() getInternalName}). May be
|
|
||||||
* <tt>null</tt>.
|
|
||||||
* @return an object to visit the byte code of the method, or <tt>null</tt>
|
|
||||||
* if this class visitor is not interested in visiting the code of
|
|
||||||
* this method.
|
|
||||||
*/
|
*/
|
||||||
public MethodVisitor visitMethod(int access, String name, String desc,
|
public MethodVisitor visitMethod(
|
||||||
String signature, String[] exceptions) {
|
final int access,
|
||||||
|
final String name,
|
||||||
|
final String descriptor,
|
||||||
|
final String signature,
|
||||||
|
final String[] exceptions) {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
return cv.visitMethod(access, name, desc, signature, exceptions);
|
return cv.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits the end of the class. This method, which is the last one to be
|
* Visits the end of the class. This method, which is the last one to be called, is used to inform
|
||||||
* called, is used to inform the visitor that all the fields and methods of
|
* the visitor that all the fields and methods of the class have been visited.
|
||||||
* the class have been visited.
|
|
||||||
*/
|
*/
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
if (cv != null) {
|
if (cv != null) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file:
|
||||||
|
*
|
||||||
|
* ASM: a very small and fast Java bytecode manipulation framework
|
||||||
|
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constant whose value is computed at runtime, with a bootstrap method.
|
||||||
|
*
|
||||||
|
* @author Remi Forax
|
||||||
|
*/
|
||||||
|
public final class ConstantDynamic {
|
||||||
|
|
||||||
|
/** The constant name (can be arbitrary). */
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/** The constant type (must be a field descriptor). */
|
||||||
|
private final String descriptor;
|
||||||
|
|
||||||
|
/** The bootstrap method to use to compute the constant value at runtime. */
|
||||||
|
private final Handle bootstrapMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The arguments to pass to the bootstrap method, in order to compute the constant value at
|
||||||
|
* runtime.
|
||||||
|
*/
|
||||||
|
private final Object[] bootstrapMethodArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@link ConstantDynamic}.
|
||||||
|
*
|
||||||
|
* @param name the constant name (can be arbitrary).
|
||||||
|
* @param descriptor the constant type (must be a field descriptor).
|
||||||
|
* @param bootstrapMethod the bootstrap method to use to compute the constant value at runtime.
|
||||||
|
* @param bootstrapMethodArguments the arguments to pass to the bootstrap method, in order to
|
||||||
|
* compute the constant value at runtime.
|
||||||
|
*/
|
||||||
|
public ConstantDynamic(
|
||||||
|
final String name,
|
||||||
|
final String descriptor,
|
||||||
|
final Handle bootstrapMethod,
|
||||||
|
final Object... bootstrapMethodArguments) {
|
||||||
|
this.name = name;
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
this.bootstrapMethod = bootstrapMethod;
|
||||||
|
this.bootstrapMethodArguments = bootstrapMethodArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of this constant.
|
||||||
|
*
|
||||||
|
* @return the name of this constant.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this constant.
|
||||||
|
*
|
||||||
|
* @return the type of this constant, as a field descriptor.
|
||||||
|
*/
|
||||||
|
public String getDescriptor() {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bootstrap method used to compute the value of this constant.
|
||||||
|
*
|
||||||
|
* @return the bootstrap method used to compute the value of this constant.
|
||||||
|
*/
|
||||||
|
public Handle getBootstrapMethod() {
|
||||||
|
return bootstrapMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of arguments passed to the bootstrap method, in order to compute the value
|
||||||
|
* of this constant.
|
||||||
|
*
|
||||||
|
* @return the number of arguments passed to the bootstrap method, in order to compute the value
|
||||||
|
* of this constant.
|
||||||
|
*/
|
||||||
|
public int getBootstrapMethodArgumentCount() {
|
||||||
|
return bootstrapMethodArguments.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an argument passed to the bootstrap method, in order to compute the value of this
|
||||||
|
* constant.
|
||||||
|
*
|
||||||
|
* @param index an argument index, between 0 and {@link #getBootstrapMethodArgumentCount()}
|
||||||
|
* (exclusive).
|
||||||
|
* @return the argument passed to the bootstrap method, with the given index.
|
||||||
|
*/
|
||||||
|
public Object getBootstrapMethodArgument(final int index) {
|
||||||
|
return bootstrapMethodArguments[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the arguments to pass to the bootstrap method, in order to compute the value of this
|
||||||
|
* constant. WARNING: this array must not be modified, and must not be returned to the user.
|
||||||
|
*
|
||||||
|
* @return the arguments to pass to the bootstrap method, in order to compute the value of this
|
||||||
|
* constant.
|
||||||
|
*/
|
||||||
|
Object[] getBootstrapMethodArgumentsUnsafe() {
|
||||||
|
return bootstrapMethodArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of this constant.
|
||||||
|
*
|
||||||
|
* @return the size of this constant, i.e., 2 for {@code long} and {@code double}, 1 otherwise.
|
||||||
|
*/
|
||||||
|
public int getSize() {
|
||||||
|
char firstCharOfDescriptor = descriptor.charAt(0);
|
||||||
|
return (firstCharOfDescriptor == 'J' || firstCharOfDescriptor == 'D') ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object object) {
|
||||||
|
if (object == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(object instanceof ConstantDynamic)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConstantDynamic constantDynamic = (ConstantDynamic) object;
|
||||||
|
return name.equals(constantDynamic.name)
|
||||||
|
&& descriptor.equals(constantDynamic.descriptor)
|
||||||
|
&& bootstrapMethod.equals(constantDynamic.bootstrapMethod)
|
||||||
|
&& Arrays.equals(bootstrapMethodArguments, constantDynamic.bootstrapMethodArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return name.hashCode()
|
||||||
|
^ Integer.rotateLeft(descriptor.hashCode(), 8)
|
||||||
|
^ Integer.rotateLeft(bootstrapMethod.hashCode(), 16)
|
||||||
|
^ Integer.rotateLeft(Arrays.hashCode(bootstrapMethodArguments), 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name
|
||||||
|
+ " : "
|
||||||
|
+ descriptor
|
||||||
|
+ ' '
|
||||||
|
+ bootstrapMethod
|
||||||
|
+ ' '
|
||||||
|
+ Arrays.toString(bootstrapMethodArguments);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,208 @@
|
|||||||
|
/*
|
||||||
|
* 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file:
|
||||||
|
*
|
||||||
|
* ASM: a very small and fast Java bytecode manipulation framework
|
||||||
|
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines additional JVM opcodes, access flags and constants which are not part of the ASM public
|
||||||
|
* API.
|
||||||
|
*
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html">JVMS 6</a>
|
||||||
|
* @author Eric Bruneton
|
||||||
|
*/
|
||||||
|
final class Constants implements Opcodes {
|
||||||
|
|
||||||
|
// The ClassFile attribute names, in the order they are defined in
|
||||||
|
// https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300.
|
||||||
|
|
||||||
|
static final String CONSTANT_VALUE = "ConstantValue";
|
||||||
|
static final String CODE = "Code";
|
||||||
|
static final String STACK_MAP_TABLE = "StackMapTable";
|
||||||
|
static final String EXCEPTIONS = "Exceptions";
|
||||||
|
static final String INNER_CLASSES = "InnerClasses";
|
||||||
|
static final String ENCLOSING_METHOD = "EnclosingMethod";
|
||||||
|
static final String SYNTHETIC = "Synthetic";
|
||||||
|
static final String SIGNATURE = "Signature";
|
||||||
|
static final String SOURCE_FILE = "SourceFile";
|
||||||
|
static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
|
||||||
|
static final String LINE_NUMBER_TABLE = "LineNumberTable";
|
||||||
|
static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable";
|
||||||
|
static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable";
|
||||||
|
static final String DEPRECATED = "Deprecated";
|
||||||
|
static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
|
||||||
|
static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
|
||||||
|
static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
|
||||||
|
static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
|
||||||
|
"RuntimeInvisibleParameterAnnotations";
|
||||||
|
static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
|
||||||
|
static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations";
|
||||||
|
static final String ANNOTATION_DEFAULT = "AnnotationDefault";
|
||||||
|
static final String BOOTSTRAP_METHODS = "BootstrapMethods";
|
||||||
|
static final String METHOD_PARAMETERS = "MethodParameters";
|
||||||
|
static final String MODULE = "Module";
|
||||||
|
static final String MODULE_PACKAGES = "ModulePackages";
|
||||||
|
static final String MODULE_MAIN_CLASS = "ModuleMainClass";
|
||||||
|
static final String NEST_HOST = "NestHost";
|
||||||
|
static final String NEST_MEMBERS = "NestMembers";
|
||||||
|
|
||||||
|
// ASM specific access flags.
|
||||||
|
// WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard
|
||||||
|
// access flags, and also to make sure that these flags are automatically filtered out when
|
||||||
|
// written in class files (because access flags are stored using 16 bits only).
|
||||||
|
|
||||||
|
static final int ACC_CONSTRUCTOR = 0x40000; // method access flag.
|
||||||
|
|
||||||
|
// ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A frame inserted between already existing frames. This internal stack map frame type (in
|
||||||
|
* addition to the ones declared in {@link Opcodes}) can only be used if the frame content can be
|
||||||
|
* computed from the previous existing frame and from the instructions between this existing frame
|
||||||
|
* and the inserted one, without any knowledge of the type hierarchy. This kind of frame is only
|
||||||
|
* used when an unconditional jump is inserted in a method while expanding an ASM specific
|
||||||
|
* instruction. Keep in sync with Opcodes.java.
|
||||||
|
*/
|
||||||
|
static final int F_INSERT = 256;
|
||||||
|
|
||||||
|
// The JVM opcode values which are not part of the ASM public API.
|
||||||
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html.
|
||||||
|
|
||||||
|
static final int LDC_W = 19;
|
||||||
|
static final int LDC2_W = 20;
|
||||||
|
static final int ILOAD_0 = 26;
|
||||||
|
static final int ILOAD_1 = 27;
|
||||||
|
static final int ILOAD_2 = 28;
|
||||||
|
static final int ILOAD_3 = 29;
|
||||||
|
static final int LLOAD_0 = 30;
|
||||||
|
static final int LLOAD_1 = 31;
|
||||||
|
static final int LLOAD_2 = 32;
|
||||||
|
static final int LLOAD_3 = 33;
|
||||||
|
static final int FLOAD_0 = 34;
|
||||||
|
static final int FLOAD_1 = 35;
|
||||||
|
static final int FLOAD_2 = 36;
|
||||||
|
static final int FLOAD_3 = 37;
|
||||||
|
static final int DLOAD_0 = 38;
|
||||||
|
static final int DLOAD_1 = 39;
|
||||||
|
static final int DLOAD_2 = 40;
|
||||||
|
static final int DLOAD_3 = 41;
|
||||||
|
static final int ALOAD_0 = 42;
|
||||||
|
static final int ALOAD_1 = 43;
|
||||||
|
static final int ALOAD_2 = 44;
|
||||||
|
static final int ALOAD_3 = 45;
|
||||||
|
static final int ISTORE_0 = 59;
|
||||||
|
static final int ISTORE_1 = 60;
|
||||||
|
static final int ISTORE_2 = 61;
|
||||||
|
static final int ISTORE_3 = 62;
|
||||||
|
static final int LSTORE_0 = 63;
|
||||||
|
static final int LSTORE_1 = 64;
|
||||||
|
static final int LSTORE_2 = 65;
|
||||||
|
static final int LSTORE_3 = 66;
|
||||||
|
static final int FSTORE_0 = 67;
|
||||||
|
static final int FSTORE_1 = 68;
|
||||||
|
static final int FSTORE_2 = 69;
|
||||||
|
static final int FSTORE_3 = 70;
|
||||||
|
static final int DSTORE_0 = 71;
|
||||||
|
static final int DSTORE_1 = 72;
|
||||||
|
static final int DSTORE_2 = 73;
|
||||||
|
static final int DSTORE_3 = 74;
|
||||||
|
static final int ASTORE_0 = 75;
|
||||||
|
static final int ASTORE_1 = 76;
|
||||||
|
static final int ASTORE_2 = 77;
|
||||||
|
static final int ASTORE_3 = 78;
|
||||||
|
static final int WIDE = 196;
|
||||||
|
static final int GOTO_W = 200;
|
||||||
|
static final int JSR_W = 201;
|
||||||
|
|
||||||
|
// Constants to convert between normal and wide jump instructions.
|
||||||
|
|
||||||
|
// The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP.
|
||||||
|
static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO;
|
||||||
|
|
||||||
|
// Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa.
|
||||||
|
|
||||||
|
// The delta between the ASM_IFEQ, ..., ASM_IF_ACMPNE, ASM_GOTO and ASM_JSR opcodes
|
||||||
|
// and IFEQ, ..., IF_ACMPNE, GOTO and JSR.
|
||||||
|
static final int ASM_OPCODE_DELTA = 49;
|
||||||
|
|
||||||
|
// The delta between the ASM_IFNULL and ASM_IFNONNULL opcodes and IFNULL and IFNONNULL.
|
||||||
|
static final int ASM_IFNULL_OPCODE_DELTA = 20;
|
||||||
|
|
||||||
|
// ASM specific opcodes, used for long forward jump instructions.
|
||||||
|
|
||||||
|
static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_JSR = JSR + ASM_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA;
|
||||||
|
static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA;
|
||||||
|
static final int ASM_GOTO_W = 220;
|
||||||
|
|
||||||
|
private Constants() {}
|
||||||
|
}
|
@ -56,7 +56,6 @@
|
|||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,111 +63,105 @@ package jdk.internal.org.objectweb.asm;
|
|||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
class Context {
|
final class Context {
|
||||||
|
|
||||||
|
/** The prototypes of the attributes that must be parsed in this class. */
|
||||||
|
Attribute[] attributePrototypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prototypes of the attributes that must be parsed for this class.
|
* The options used to parse this class. One or more of {@link ClassReader#SKIP_CODE}, {@link
|
||||||
|
* ClassReader#SKIP_DEBUG}, {@link ClassReader#SKIP_FRAMES}, {@link ClassReader#EXPAND_FRAMES} or
|
||||||
|
* {@link ClassReader#EXPAND_ASM_INSNS}.
|
||||||
*/
|
*/
|
||||||
Attribute[] attrs;
|
int parsingOptions;
|
||||||
|
|
||||||
|
/** The buffer used to read strings in the constant pool. */
|
||||||
|
char[] charBuffer;
|
||||||
|
|
||||||
|
// Information about the current method, i.e. the one read in the current (or latest) call
|
||||||
|
// to {@link ClassReader#readMethod()}.
|
||||||
|
|
||||||
|
/** The access flags of the current method. */
|
||||||
|
int currentMethodAccessFlags;
|
||||||
|
|
||||||
|
/** The name of the current method. */
|
||||||
|
String currentMethodName;
|
||||||
|
|
||||||
|
/** The descriptor of the current method. */
|
||||||
|
String currentMethodDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link ClassReader} option flags for the parsing of this class.
|
* The labels of the current method, indexed by bytecode offset (only bytecode offsets for which a
|
||||||
|
* label is needed have a non null associated Label).
|
||||||
*/
|
*/
|
||||||
int flags;
|
Label[] currentMethodLabels;
|
||||||
|
|
||||||
|
// Information about the current type annotation target, i.e. the one read in the current
|
||||||
|
// (or latest) call to {@link ClassReader#readAnnotationTarget()}.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer used to read strings.
|
* The target_type and target_info of the current type annotation target, encoded as described in
|
||||||
|
* {@link TypeReference}.
|
||||||
*/
|
*/
|
||||||
char[] buffer;
|
int currentTypeAnnotationTarget;
|
||||||
|
|
||||||
|
/** The target_path of the current type annotation target. */
|
||||||
|
TypePath currentTypeAnnotationTargetPath;
|
||||||
|
|
||||||
|
/** The start of each local variable range in the current local variable annotation. */
|
||||||
|
Label[] currentLocalVariableAnnotationRangeStarts;
|
||||||
|
|
||||||
|
/** The end of each local variable range in the current local variable annotation. */
|
||||||
|
Label[] currentLocalVariableAnnotationRangeEnds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The start index of each bootstrap method.
|
* The local variable index of each local variable range in the current local variable annotation.
|
||||||
*/
|
*/
|
||||||
int[] bootstrapMethods;
|
int[] currentLocalVariableAnnotationRangeIndices;
|
||||||
|
|
||||||
|
// Information about the current stack map frame, i.e. the one read in the current (or latest)
|
||||||
|
// call to {@link ClassReader#readFrame()}.
|
||||||
|
|
||||||
|
/** The bytecode offset of the current stack map frame. */
|
||||||
|
int currentFrameOffset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The access flags of the method currently being parsed.
|
* The type of the current stack map frame. One of {@link Opcodes#F_FULL}, {@link
|
||||||
|
* Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or {@link Opcodes#F_SAME1}.
|
||||||
*/
|
*/
|
||||||
int access;
|
int currentFrameType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the method currently being parsed.
|
* The number of local variable types in the current stack map frame. Each type is represented
|
||||||
|
* with a single array element (even long and double).
|
||||||
*/
|
*/
|
||||||
String name;
|
int currentFrameLocalCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The descriptor of the method currently being parsed.
|
* The delta number of local variable types in the current stack map frame (each type is
|
||||||
|
* represented with a single array element - even long and double). This is the number of local
|
||||||
|
* variable types in this frame, minus the number of local variable types in the previous frame.
|
||||||
*/
|
*/
|
||||||
String desc;
|
int currentFrameLocalCountDelta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The label objects, indexed by bytecode offset, of the method currently
|
* The types of the local variables in the current stack map frame. Each type is represented with
|
||||||
* being parsed (only bytecode offsets for which a label is needed have a
|
* a single array element (even long and double), using the format described in {@link
|
||||||
* non null associated Label object).
|
* MethodVisitor#visitFrame}. Depending on {@link #currentFrameType}, this contains the types of
|
||||||
|
* all the local variables, or only those of the additional ones (compared to the previous frame).
|
||||||
*/
|
*/
|
||||||
Label[] labels;
|
Object[] currentFrameLocalTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The target of the type annotation currently being parsed.
|
* The number stack element types in the current stack map frame. Each type is represented with a
|
||||||
|
* single array element (even long and double).
|
||||||
*/
|
*/
|
||||||
int typeRef;
|
int currentFrameStackCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path of the type annotation currently being parsed.
|
* The types of the stack elements in the current stack map frame. Each type is represented with a
|
||||||
|
* single array element (even long and double), using the format described in {@link
|
||||||
|
* MethodVisitor#visitFrame}.
|
||||||
*/
|
*/
|
||||||
TypePath typePath;
|
Object[] currentFrameStackTypes;
|
||||||
|
|
||||||
/**
|
|
||||||
* The offset of the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
int offset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The labels corresponding to the start of the local variable ranges in the
|
|
||||||
* local variable type annotation currently being parsed.
|
|
||||||
*/
|
|
||||||
Label[] start;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The labels corresponding to the end of the local variable ranges in the
|
|
||||||
* local variable type annotation currently being parsed.
|
|
||||||
*/
|
|
||||||
Label[] end;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The local variable indices for each local variable range in the local
|
|
||||||
* variable type annotation currently being parsed.
|
|
||||||
*/
|
|
||||||
int[] index;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The encoding of the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
int mode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of locals in the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
int localCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number locals in the latest stack map frame that has been parsed,
|
|
||||||
* minus the number of locals in the previous frame.
|
|
||||||
*/
|
|
||||||
int localDiff;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The local values of the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
Object[] local;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The stack size of the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
int stackCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The stack values of the latest stack map frame that has been parsed.
|
|
||||||
*/
|
|
||||||
Object[] stack;
|
|
||||||
}
|
}
|
||||||
|
@ -56,30 +56,31 @@
|
|||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the input stack map frame at the "current" instruction of a
|
* Information about the input stack map frame at the "current" instruction of a method. This is
|
||||||
* method. This is implemented as a Frame subclass for a "basic block"
|
* implemented as a Frame subclass for a "basic block" containing only one instruction.
|
||||||
* containing only one instruction.
|
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
class CurrentFrame extends Frame {
|
final class CurrentFrame extends Frame {
|
||||||
|
|
||||||
|
CurrentFrame(final Label owner) {
|
||||||
|
super(owner);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this CurrentFrame to the input stack map frame of the next "current"
|
* Sets this CurrentFrame to the input stack map frame of the next "current" instruction, i.e. the
|
||||||
* instruction, i.e. the instruction just after the given one. It is assumed
|
* instruction just after the given one. It is assumed that the value of this object when this
|
||||||
* that the value of this object when this method is called is the stack map
|
* method is called is the stack map frame status just before the given instruction is executed.
|
||||||
* frame status just before the given instruction is executed.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
void execute(int opcode, int arg, ClassWriter cw, Item item) {
|
void execute(
|
||||||
super.execute(opcode, arg, cw, item);
|
final int opcode, final int arg, final Symbol symbolArg, final SymbolTable symbolTable) {
|
||||||
Frame successor = new Frame();
|
super.execute(opcode, arg, symbolArg, symbolTable);
|
||||||
merge(cw, successor, 0);
|
Frame successor = new Frame(null);
|
||||||
set(successor);
|
merge(symbolTable, successor, 0);
|
||||||
owner.inputStackTop = 0;
|
copyFrom(successor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,46 +59,64 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An edge in the control flow graph of a method body. See {@link Label Label}.
|
* An edge in the control flow graph of a method. Each node of this graph is a basic block,
|
||||||
|
* represented with the Label corresponding to its first instruction. Each edge goes from one node
|
||||||
|
* to another, i.e. from one basic block to another (called the predecessor and successor blocks,
|
||||||
|
* respectively). An edge corresponds either to a jump or ret instruction or to an exception
|
||||||
|
* handler.
|
||||||
*
|
*
|
||||||
|
* @see Label
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
class Edge {
|
final class Edge {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denotes a normal control flow graph edge.
|
* A control flow graph edge corresponding to a jump or ret instruction. Only used with {@link
|
||||||
|
* ClassWriter#COMPUTE_FRAMES}.
|
||||||
*/
|
*/
|
||||||
static final int NORMAL = 0;
|
static final int JUMP = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Denotes a control flow graph edge corresponding to an exception handler.
|
* A control flow graph edge corresponding to an exception handler. Only used with {@link
|
||||||
* More precisely any {@link Edge} whose {@link #info} is strictly positive
|
* ClassWriter#COMPUTE_MAXS}.
|
||||||
* corresponds to an exception handler. The actual value of {@link #info} is
|
|
||||||
* the index, in the {@link ClassWriter} type table, of the exception that
|
|
||||||
* is catched.
|
|
||||||
*/
|
*/
|
||||||
static final int EXCEPTION = 0x7FFFFFFF;
|
static final int EXCEPTION = 0x7FFFFFFF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about this control flow graph edge. If
|
* Information about this control flow graph edge.
|
||||||
* {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative)
|
*
|
||||||
* stack size in the basic block from which this edge originates. This size
|
* <ul>
|
||||||
* is equal to the stack size at the "jump" instruction to which this edge
|
* <li>If {@link ClassWriter#COMPUTE_MAXS} is used, this field contains either a stack size
|
||||||
* corresponds, relatively to the stack size at the beginning of the
|
* delta (for an edge corresponding to a jump instruction), or the value EXCEPTION (for an
|
||||||
* originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used,
|
* edge corresponding to an exception handler). The stack size delta is the stack size just
|
||||||
* this field is the kind of this control flow graph edge (i.e. NORMAL or
|
* after the jump instruction, minus the stack size at the beginning of the predecessor
|
||||||
* EXCEPTION).
|
* basic block, i.e. the one containing the jump instruction.
|
||||||
|
* <li>If {@link ClassWriter#COMPUTE_FRAMES} is used, this field contains either the value JUMP
|
||||||
|
* (for an edge corresponding to a jump instruction), or the index, in the {@link
|
||||||
|
* ClassWriter} type table, of the exception type that is handled (for an edge corresponding
|
||||||
|
* to an exception handler).
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
int info;
|
final int info;
|
||||||
|
|
||||||
|
/** The successor block of this control flow graph edge. */
|
||||||
|
final Label successor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The successor block of the basic block from which this edge originates.
|
* The next edge in the list of outgoing edges of a basic block. See {@link Label#outgoingEdges}.
|
||||||
*/
|
*/
|
||||||
Label successor;
|
Edge nextEdge;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The next edge in the list of successors of the originating basic block.
|
* Constructs a new Edge.
|
||||||
* See {@link Label#successors successors}.
|
*
|
||||||
|
* @param info see {@link #info}.
|
||||||
|
* @param successor see {@link #successor}.
|
||||||
|
* @param nextEdge see {@link #nextEdge}.
|
||||||
*/
|
*/
|
||||||
Edge next;
|
Edge(final int info, final Label successor, final Edge nextEdge) {
|
||||||
|
this.info = info;
|
||||||
|
this.successor = successor;
|
||||||
|
this.nextEdge = nextEdge;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,32 +59,28 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A visitor to visit a Java field. The methods of this class must be called in
|
* A visitor to visit a Java field. The methods of this class must be called in the following order:
|
||||||
* the following order: ( <tt>visitAnnotation</tt> |
|
* ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code
|
||||||
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
|
* visitEnd}.
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
public abstract class FieldVisitor {
|
public abstract class FieldVisitor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ASM API version implemented by this visitor. The value of this field
|
* The ASM API version implemented by this visitor. The value of this field must be one of {@link
|
||||||
* must be one of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
*/
|
*/
|
||||||
protected final int api;
|
protected final int api;
|
||||||
|
|
||||||
/**
|
/** The field visitor to which this visitor must delegate method calls. May be null. */
|
||||||
* The field visitor to which this visitor must delegate method calls. May
|
|
||||||
* be null.
|
|
||||||
*/
|
|
||||||
protected FieldVisitor fv;
|
protected FieldVisitor fv;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link FieldVisitor}.
|
* Constructs a new {@link FieldVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
|
||||||
*/
|
*/
|
||||||
public FieldVisitor(final int api) {
|
public FieldVisitor(final int api) {
|
||||||
this(api, null);
|
this(api, null);
|
||||||
@ -93,34 +89,30 @@ public abstract class FieldVisitor {
|
|||||||
/**
|
/**
|
||||||
* Constructs a new {@link FieldVisitor}.
|
* Constructs a new {@link FieldVisitor}.
|
||||||
*
|
*
|
||||||
* @param api
|
* @param api the ASM API version implemented by this visitor. Must be one of {@link
|
||||||
* the ASM API version implemented by this visitor. Must be one
|
* Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}.
|
||||||
* of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
|
* @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be
|
||||||
* @param fv
|
* null.
|
||||||
* the field visitor to which this visitor must delegate method
|
|
||||||
* calls. May be null.
|
|
||||||
*/
|
*/
|
||||||
public FieldVisitor(final int api, final FieldVisitor fv) {
|
public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
|
||||||
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
|
if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
this.api = api;
|
this.api = api;
|
||||||
this.fv = fv;
|
this.fv = fieldVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits an annotation of the field.
|
* Visits an annotation of the field.
|
||||||
*
|
*
|
||||||
* @param desc
|
* @param descriptor the class descriptor of the annotation class.
|
||||||
* the class descriptor of the annotation class.
|
* @param visible {@literal true} if the annotation is visible at runtime.
|
||||||
* @param visible
|
* @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||||||
* <tt>true</tt> if the annotation is visible at runtime.
|
* interested in visiting this annotation.
|
||||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
|
||||||
* this visitor is not interested in visiting this annotation.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
|
||||||
if (fv != null) {
|
if (fv != null) {
|
||||||
return fv.visitAnnotation(desc, visible);
|
return fv.visitAnnotation(descriptor, visible);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -128,28 +120,23 @@ public abstract class FieldVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits an annotation on the type of the field.
|
* Visits an annotation on the type of the field.
|
||||||
*
|
*
|
||||||
* @param typeRef
|
* @param typeRef a reference to the annotated type. The sort of this type reference must be
|
||||||
* a reference to the annotated type. The sort of this type
|
* {@link TypeReference#FIELD}. See {@link TypeReference}.
|
||||||
* reference must be {@link TypeReference#FIELD FIELD}. See
|
* @param typePath the path to the annotated type argument, wildcard bound, array element type, or
|
||||||
* {@link TypeReference}.
|
* static inner type within 'typeRef'. May be {@literal null} if the annotation targets
|
||||||
* @param typePath
|
* 'typeRef' as a whole.
|
||||||
* the path to the annotated type argument, wildcard bound, array
|
* @param descriptor the class descriptor of the annotation class.
|
||||||
* element type, or static inner type within 'typeRef'. May be
|
* @param visible {@literal true} if the annotation is visible at runtime.
|
||||||
* <tt>null</tt> if the annotation targets 'typeRef' as a whole.
|
* @return a visitor to visit the annotation values, or {@literal null} if this visitor is not
|
||||||
* @param desc
|
* interested in visiting this annotation.
|
||||||
* the class descriptor of the annotation class.
|
|
||||||
* @param visible
|
|
||||||
* <tt>true</tt> if the annotation is visible at runtime.
|
|
||||||
* @return a visitor to visit the annotation values, or <tt>null</tt> if
|
|
||||||
* this visitor is not interested in visiting this annotation.
|
|
||||||
*/
|
*/
|
||||||
public AnnotationVisitor visitTypeAnnotation(int typeRef,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
TypePath typePath, String desc, boolean visible) {
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||||||
if (api < Opcodes.ASM5) {
|
if (api < Opcodes.ASM5) {
|
||||||
throw new RuntimeException();
|
throw new UnsupportedOperationException("This feature requires ASM5");
|
||||||
}
|
}
|
||||||
if (fv != null) {
|
if (fv != null) {
|
||||||
return fv.visitTypeAnnotation(typeRef, typePath, desc, visible);
|
return fv.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -157,19 +144,17 @@ public abstract class FieldVisitor {
|
|||||||
/**
|
/**
|
||||||
* Visits a non standard attribute of the field.
|
* Visits a non standard attribute of the field.
|
||||||
*
|
*
|
||||||
* @param attr
|
* @param attribute an attribute.
|
||||||
* an attribute.
|
|
||||||
*/
|
*/
|
||||||
public void visitAttribute(Attribute attr) {
|
public void visitAttribute(final Attribute attribute) {
|
||||||
if (fv != null) {
|
if (fv != null) {
|
||||||
fv.visitAttribute(attr);
|
fv.visitAttribute(attribute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits the end of the field. This method, which is the last one to be
|
* Visits the end of the field. This method, which is the last one to be called, is used to inform
|
||||||
* called, is used to inform the visitor that all the annotations and
|
* the visitor that all the annotations and attributes of the field have been visited.
|
||||||
* attributes of the field have been visited.
|
|
||||||
*/
|
*/
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
if (fv != null) {
|
if (fv != null) {
|
||||||
|
@ -59,294 +59,319 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link FieldVisitor} that generates Java fields in bytecode form.
|
* A {@link FieldVisitor} that generates a corresponding 'field_info' structure, as defined in the
|
||||||
|
* Java Virtual Machine Specification (JVMS).
|
||||||
*
|
*
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5">JVMS
|
||||||
|
* 4.5</a>
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
final class FieldWriter extends FieldVisitor {
|
final class FieldWriter extends FieldVisitor {
|
||||||
|
|
||||||
/**
|
/** Where the constants used in this FieldWriter must be stored. */
|
||||||
* The class writer to which this field must be added.
|
private final SymbolTable symbolTable;
|
||||||
*/
|
|
||||||
private final ClassWriter cw;
|
// Note: fields are ordered as in the field_info structure, and those related to attributes are
|
||||||
|
// ordered as in Section 4.7 of the JVMS.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access flags of this field.
|
* The access_flags field of the field_info JVMS structure. This field can contain ASM specific
|
||||||
|
* access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the
|
||||||
|
* ClassFile structure.
|
||||||
*/
|
*/
|
||||||
private final int access;
|
private final int accessFlags;
|
||||||
|
|
||||||
|
/** The name_index field of the field_info JVMS structure. */
|
||||||
|
private final int nameIndex;
|
||||||
|
|
||||||
|
/** The descriptor_index field of the field_info JVMS structure. */
|
||||||
|
private final int descriptorIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the constant pool item that contains the name of this
|
* The signature_index field of the Signature attribute of this field_info, or 0 if there is no
|
||||||
* method.
|
* Signature attribute.
|
||||||
*/
|
*/
|
||||||
private final int name;
|
private int signatureIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the constant pool item that contains the descriptor of this
|
* The constantvalue_index field of the ConstantValue attribute of this field_info, or 0 if there
|
||||||
* field.
|
* is no ConstantValue attribute.
|
||||||
*/
|
*/
|
||||||
private final int desc;
|
private int constantValueIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the constant pool item that contains the signature of this
|
* The last runtime visible annotation of this field. The previous ones can be accessed with the
|
||||||
* field.
|
* {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||||||
*/
|
*/
|
||||||
private int signature;
|
private AnnotationWriter lastRuntimeVisibleAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of the constant pool item that contains the constant value of
|
* The last runtime invisible annotation of this field. The previous ones can be accessed with the
|
||||||
* this field.
|
* {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||||||
*/
|
*/
|
||||||
private int value;
|
private AnnotationWriter lastRuntimeInvisibleAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The runtime visible annotations of this field. May be <tt>null</tt>.
|
* The last runtime visible type annotation of this field. The previous ones can be accessed with
|
||||||
|
* the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||||||
*/
|
*/
|
||||||
private AnnotationWriter anns;
|
private AnnotationWriter lastRuntimeVisibleTypeAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The runtime invisible annotations of this field. May be <tt>null</tt>.
|
* The last runtime invisible type annotation of this field. The previous ones can be accessed
|
||||||
|
* with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}.
|
||||||
*/
|
*/
|
||||||
private AnnotationWriter ianns;
|
private AnnotationWriter lastRuntimeInvisibleTypeAnnotation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The runtime visible type annotations of this field. May be <tt>null</tt>.
|
* The first non standard attribute of this field. The next ones can be accessed with the {@link
|
||||||
|
* Attribute#nextAttribute} field. May be {@literal null}.
|
||||||
|
*
|
||||||
|
* <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
|
||||||
|
* firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link
|
||||||
|
* #putFieldInfo} method writes the attributes in the order defined by this list, i.e. in the
|
||||||
|
* reverse order specified by the user.
|
||||||
*/
|
*/
|
||||||
private AnnotationWriter tanns;
|
private Attribute firstAttribute;
|
||||||
|
|
||||||
/**
|
// -----------------------------------------------------------------------------------------------
|
||||||
* The runtime invisible type annotations of this field. May be
|
|
||||||
* <tt>null</tt>.
|
|
||||||
*/
|
|
||||||
private AnnotationWriter itanns;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The non standard attributes of this field. May be <tt>null</tt>.
|
|
||||||
*/
|
|
||||||
private Attribute attrs;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Constructor
|
// Constructor
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link FieldWriter}.
|
* Constructs a new {@link FieldWriter}.
|
||||||
*
|
*
|
||||||
* @param cw
|
* @param symbolTable where the constants used in this FieldWriter must be stored.
|
||||||
* the class writer to which this field must be added.
|
* @param access the field's access flags (see {@link Opcodes}).
|
||||||
* @param access
|
* @param name the field's name.
|
||||||
* the field's access flags (see {@link Opcodes}).
|
* @param descriptor the field's descriptor (see {@link Type}).
|
||||||
* @param name
|
* @param signature the field's signature. May be {@literal null}.
|
||||||
* the field's name.
|
* @param constantValue the field's constant value. May be {@literal null}.
|
||||||
* @param desc
|
|
||||||
* the field's descriptor (see {@link Type}).
|
|
||||||
* @param signature
|
|
||||||
* the field's signature. May be <tt>null</tt>.
|
|
||||||
* @param value
|
|
||||||
* the field's constant value. May be <tt>null</tt>.
|
|
||||||
*/
|
*/
|
||||||
FieldWriter(final ClassWriter cw, final int access, final String name,
|
FieldWriter(
|
||||||
final String desc, final String signature, final Object value) {
|
final SymbolTable symbolTable,
|
||||||
super(Opcodes.ASM6);
|
final int access,
|
||||||
if (cw.firstField == null) {
|
final String name,
|
||||||
cw.firstField = this;
|
final String descriptor,
|
||||||
} else {
|
final String signature,
|
||||||
cw.lastField.fv = this;
|
final Object constantValue) {
|
||||||
}
|
super(Opcodes.ASM7);
|
||||||
cw.lastField = this;
|
this.symbolTable = symbolTable;
|
||||||
this.cw = cw;
|
this.accessFlags = access;
|
||||||
this.access = access;
|
this.nameIndex = symbolTable.addConstantUtf8(name);
|
||||||
this.name = cw.newUTF8(name);
|
this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
|
||||||
this.desc = cw.newUTF8(desc);
|
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
this.signature = cw.newUTF8(signature);
|
this.signatureIndex = symbolTable.addConstantUtf8(signature);
|
||||||
}
|
}
|
||||||
if (value != null) {
|
if (constantValue != null) {
|
||||||
this.value = cw.newConstItem(value).index;
|
this.constantValueIndex = symbolTable.addConstant(constantValue).index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Implementation of the FieldVisitor abstract class
|
// Implementation of the FieldVisitor abstract class
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(final String desc,
|
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
|
||||||
final boolean visible) {
|
// Create a ByteVector to hold an 'annotation' JVMS structure.
|
||||||
ByteVector bv = new ByteVector();
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.
|
||||||
// write type, and reserve space for values count
|
ByteVector annotation = new ByteVector();
|
||||||
bv.putShort(cw.newUTF8(desc)).putShort(0);
|
// Write type_index and reserve space for num_element_value_pairs.
|
||||||
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
|
annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||||||
if (visible) {
|
if (visible) {
|
||||||
aw.next = anns;
|
return lastRuntimeVisibleAnnotation =
|
||||||
anns = aw;
|
new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation);
|
||||||
} else {
|
} else {
|
||||||
aw.next = ianns;
|
return lastRuntimeInvisibleAnnotation =
|
||||||
ianns = aw;
|
new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation);
|
||||||
}
|
}
|
||||||
return aw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitTypeAnnotation(final int typeRef,
|
public AnnotationVisitor visitTypeAnnotation(
|
||||||
final TypePath typePath, final String desc, final boolean visible) {
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||||||
ByteVector bv = new ByteVector();
|
// Create a ByteVector to hold a 'type_annotation' JVMS structure.
|
||||||
// write target_type and target_info
|
// See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20.
|
||||||
AnnotationWriter.putTarget(typeRef, typePath, bv);
|
ByteVector typeAnnotation = new ByteVector();
|
||||||
// write type, and reserve space for values count
|
// Write target_type, target_info, and target_path.
|
||||||
bv.putShort(cw.newUTF8(desc)).putShort(0);
|
TypeReference.putTarget(typeRef, typeAnnotation);
|
||||||
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
|
TypePath.put(typePath, typeAnnotation);
|
||||||
bv.length - 2);
|
// Write type_index and reserve space for num_element_value_pairs.
|
||||||
|
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0);
|
||||||
if (visible) {
|
if (visible) {
|
||||||
aw.next = tanns;
|
return lastRuntimeVisibleTypeAnnotation =
|
||||||
tanns = aw;
|
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation);
|
||||||
} else {
|
} else {
|
||||||
aw.next = itanns;
|
return lastRuntimeInvisibleTypeAnnotation =
|
||||||
itanns = aw;
|
new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation);
|
||||||
}
|
}
|
||||||
return aw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitAttribute(final Attribute attr) {
|
public void visitAttribute(final Attribute attribute) {
|
||||||
attr.next = attrs;
|
// Store the attributes in the <i>reverse</i> order of their visit by this method.
|
||||||
attrs = attr;
|
attribute.nextAttribute = firstAttribute;
|
||||||
|
firstAttribute = attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitEnd() {
|
public void visitEnd() {
|
||||||
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Utility methods
|
// Utility methods
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the size of this field.
|
* Returns the size of the field_info JVMS structure generated by this FieldWriter. Also adds the
|
||||||
|
* names of the attributes of this field in the constant pool.
|
||||||
*
|
*
|
||||||
* @return the size of this field.
|
* @return the size in bytes of the field_info JVMS structure.
|
||||||
*/
|
*/
|
||||||
int getSize() {
|
int computeFieldInfoSize() {
|
||||||
|
// The access_flags, name_index, descriptor_index and attributes_count fields use 8 bytes.
|
||||||
int size = 8;
|
int size = 8;
|
||||||
if (value != 0) {
|
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
||||||
cw.newUTF8("ConstantValue");
|
if (constantValueIndex != 0) {
|
||||||
|
// ConstantValue attributes always use 8 bytes.
|
||||||
|
symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE);
|
||||||
size += 8;
|
size += 8;
|
||||||
}
|
}
|
||||||
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
|
// Before Java 1.5, synthetic fields are represented with a Synthetic attribute.
|
||||||
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0
|
||||||
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
|
&& symbolTable.getMajorVersion() < Opcodes.V1_5) {
|
||||||
cw.newUTF8("Synthetic");
|
// Synthetic attributes always use 6 bytes.
|
||||||
|
symbolTable.addConstantUtf8(Constants.SYNTHETIC);
|
||||||
size += 6;
|
size += 6;
|
||||||
}
|
}
|
||||||
}
|
if (signatureIndex != 0) {
|
||||||
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
|
// Signature attributes always use 8 bytes.
|
||||||
cw.newUTF8("Deprecated");
|
symbolTable.addConstantUtf8(Constants.SIGNATURE);
|
||||||
size += 6;
|
|
||||||
}
|
|
||||||
if (signature != 0) {
|
|
||||||
cw.newUTF8("Signature");
|
|
||||||
size += 8;
|
size += 8;
|
||||||
}
|
}
|
||||||
if (anns != null) {
|
// ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead.
|
||||||
cw.newUTF8("RuntimeVisibleAnnotations");
|
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||||
size += 8 + anns.getSize();
|
// Deprecated attributes always use 6 bytes.
|
||||||
|
symbolTable.addConstantUtf8(Constants.DEPRECATED);
|
||||||
|
size += 6;
|
||||||
}
|
}
|
||||||
if (ianns != null) {
|
if (lastRuntimeVisibleAnnotation != null) {
|
||||||
cw.newUTF8("RuntimeInvisibleAnnotations");
|
size +=
|
||||||
size += 8 + ianns.getSize();
|
lastRuntimeVisibleAnnotation.computeAnnotationsSize(
|
||||||
|
Constants.RUNTIME_VISIBLE_ANNOTATIONS);
|
||||||
}
|
}
|
||||||
if (tanns != null) {
|
if (lastRuntimeInvisibleAnnotation != null) {
|
||||||
cw.newUTF8("RuntimeVisibleTypeAnnotations");
|
size +=
|
||||||
size += 8 + tanns.getSize();
|
lastRuntimeInvisibleAnnotation.computeAnnotationsSize(
|
||||||
|
Constants.RUNTIME_INVISIBLE_ANNOTATIONS);
|
||||||
}
|
}
|
||||||
if (itanns != null) {
|
if (lastRuntimeVisibleTypeAnnotation != null) {
|
||||||
cw.newUTF8("RuntimeInvisibleTypeAnnotations");
|
size +=
|
||||||
size += 8 + itanns.getSize();
|
lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize(
|
||||||
|
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS);
|
||||||
}
|
}
|
||||||
if (attrs != null) {
|
if (lastRuntimeInvisibleTypeAnnotation != null) {
|
||||||
size += attrs.getSize(cw, null, 0, -1, -1);
|
size +=
|
||||||
|
lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize(
|
||||||
|
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
|
||||||
|
}
|
||||||
|
if (firstAttribute != null) {
|
||||||
|
size += firstAttribute.computeAttributesSize(symbolTable);
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts the content of this field into the given byte vector.
|
* Puts the content of the field_info JVMS structure generated by this FieldWriter into the given
|
||||||
|
* ByteVector.
|
||||||
*
|
*
|
||||||
* @param out
|
* @param output where the field_info structure must be put.
|
||||||
* where the content of this field must be put.
|
|
||||||
*/
|
*/
|
||||||
void put(final ByteVector out) {
|
void putFieldInfo(final ByteVector output) {
|
||||||
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
|
boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5;
|
||||||
int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
|
// Put the access_flags, name_index and descriptor_index fields.
|
||||||
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
|
int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0;
|
||||||
out.putShort(access & ~mask).putShort(name).putShort(desc);
|
output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
|
||||||
int attributeCount = 0;
|
// Compute and put the attributes_count field.
|
||||||
if (value != 0) {
|
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
||||||
++attributeCount;
|
int attributesCount = 0;
|
||||||
|
if (constantValueIndex != 0) {
|
||||||
|
++attributesCount;
|
||||||
}
|
}
|
||||||
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
|
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
||||||
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|
++attributesCount;
|
||||||
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
|
}
|
||||||
++attributeCount;
|
if (signatureIndex != 0) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if (lastRuntimeVisibleAnnotation != null) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if (lastRuntimeInvisibleAnnotation != null) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if (lastRuntimeVisibleTypeAnnotation != null) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if (lastRuntimeInvisibleTypeAnnotation != null) {
|
||||||
|
++attributesCount;
|
||||||
|
}
|
||||||
|
if (firstAttribute != null) {
|
||||||
|
attributesCount += firstAttribute.getAttributeCount();
|
||||||
|
}
|
||||||
|
output.putShort(attributesCount);
|
||||||
|
// Put the field_info attributes.
|
||||||
|
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
|
||||||
|
if (constantValueIndex != 0) {
|
||||||
|
output
|
||||||
|
.putShort(symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE))
|
||||||
|
.putInt(2)
|
||||||
|
.putShort(constantValueIndex);
|
||||||
|
}
|
||||||
|
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) {
|
||||||
|
output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0);
|
||||||
|
}
|
||||||
|
if (signatureIndex != 0) {
|
||||||
|
output
|
||||||
|
.putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE))
|
||||||
|
.putInt(2)
|
||||||
|
.putShort(signatureIndex);
|
||||||
|
}
|
||||||
|
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) {
|
||||||
|
output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0);
|
||||||
|
}
|
||||||
|
if (lastRuntimeVisibleAnnotation != null) {
|
||||||
|
lastRuntimeVisibleAnnotation.putAnnotations(
|
||||||
|
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output);
|
||||||
|
}
|
||||||
|
if (lastRuntimeInvisibleAnnotation != null) {
|
||||||
|
lastRuntimeInvisibleAnnotation.putAnnotations(
|
||||||
|
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output);
|
||||||
|
}
|
||||||
|
if (lastRuntimeVisibleTypeAnnotation != null) {
|
||||||
|
lastRuntimeVisibleTypeAnnotation.putAnnotations(
|
||||||
|
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output);
|
||||||
|
}
|
||||||
|
if (lastRuntimeInvisibleTypeAnnotation != null) {
|
||||||
|
lastRuntimeInvisibleTypeAnnotation.putAnnotations(
|
||||||
|
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output);
|
||||||
|
}
|
||||||
|
if (firstAttribute != null) {
|
||||||
|
firstAttribute.putAttributes(symbolTable, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
|
|
||||||
++attributeCount;
|
/**
|
||||||
}
|
* Collects the attributes of this field into the given set of attribute prototypes.
|
||||||
if (signature != 0) {
|
*
|
||||||
++attributeCount;
|
* @param attributePrototypes a set of attribute prototypes.
|
||||||
}
|
*/
|
||||||
if (anns != null) {
|
final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
|
||||||
++attributeCount;
|
attributePrototypes.addAttributes(firstAttribute);
|
||||||
}
|
|
||||||
if (ianns != null) {
|
|
||||||
++attributeCount;
|
|
||||||
}
|
|
||||||
if (tanns != null) {
|
|
||||||
++attributeCount;
|
|
||||||
}
|
|
||||||
if (itanns != null) {
|
|
||||||
++attributeCount;
|
|
||||||
}
|
|
||||||
if (attrs != null) {
|
|
||||||
attributeCount += attrs.getCount();
|
|
||||||
}
|
|
||||||
out.putShort(attributeCount);
|
|
||||||
if (value != 0) {
|
|
||||||
out.putShort(cw.newUTF8("ConstantValue"));
|
|
||||||
out.putInt(2).putShort(value);
|
|
||||||
}
|
|
||||||
if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
|
|
||||||
if ((cw.version & 0xFFFF) < Opcodes.V1_5
|
|
||||||
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
|
|
||||||
out.putShort(cw.newUTF8("Synthetic")).putInt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((access & Opcodes.ACC_DEPRECATED) != 0) {
|
|
||||||
out.putShort(cw.newUTF8("Deprecated")).putInt(0);
|
|
||||||
}
|
|
||||||
if (signature != 0) {
|
|
||||||
out.putShort(cw.newUTF8("Signature"));
|
|
||||||
out.putInt(2).putShort(signature);
|
|
||||||
}
|
|
||||||
if (anns != null) {
|
|
||||||
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
|
|
||||||
anns.put(out);
|
|
||||||
}
|
|
||||||
if (ianns != null) {
|
|
||||||
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
|
|
||||||
ianns.put(out);
|
|
||||||
}
|
|
||||||
if (tanns != null) {
|
|
||||||
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
|
|
||||||
tanns.put(out);
|
|
||||||
}
|
|
||||||
if (itanns != null) {
|
|
||||||
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
|
|
||||||
itanns.put(out);
|
|
||||||
}
|
|
||||||
if (attrs != null) {
|
|
||||||
attrs.put(cw, null, 0, -1, -1, out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -56,7 +56,6 @@
|
|||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,117 +67,88 @@ package jdk.internal.org.objectweb.asm;
|
|||||||
public final class Handle {
|
public final class Handle {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The kind of field or method designated by this Handle. Should be
|
* The kind of field or method designated by this Handle. Should be {@link Opcodes#H_GETFIELD},
|
||||||
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
|
* {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link
|
||||||
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
|
* Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL},
|
||||||
* {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
|
* {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||||
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or
|
|
||||||
* {@link Opcodes#H_INVOKEINTERFACE}.
|
|
||||||
*/
|
*/
|
||||||
final int tag;
|
private final int tag;
|
||||||
|
|
||||||
/**
|
/** The internal name of the class that owns the field or method designated by this handle. */
|
||||||
* The internal name of the class that owns the field or method designated
|
private final String owner;
|
||||||
* by this handle.
|
|
||||||
*/
|
|
||||||
final String owner;
|
|
||||||
|
|
||||||
/**
|
/** The name of the field or method designated by this handle. */
|
||||||
* The name of the field or method designated by this handle.
|
private final String name;
|
||||||
*/
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/**
|
/** The descriptor of the field or method designated by this handle. */
|
||||||
* The descriptor of the field or method designated by this handle.
|
private final String descriptor;
|
||||||
*/
|
|
||||||
final String desc;
|
|
||||||
|
|
||||||
|
/** Whether the owner is an interface or not. */
|
||||||
/**
|
private final boolean isInterface;
|
||||||
* Indicate if the owner is an interface or not.
|
|
||||||
*/
|
|
||||||
final boolean itf;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new field or method handle.
|
* Constructs a new field or method handle.
|
||||||
*
|
*
|
||||||
* @param tag
|
* @param tag the kind of field or method designated by this Handle. Must be {@link
|
||||||
* the kind of field or method designated by this Handle. Must be
|
* Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link
|
||||||
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
|
* Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
|
||||||
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
|
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||||
* {@link Opcodes#H_INVOKEVIRTUAL},
|
* Opcodes#H_INVOKEINTERFACE}.
|
||||||
* {@link Opcodes#H_INVOKESTATIC},
|
* @param owner the internal name of the class that owns the field or method designated by this
|
||||||
* {@link Opcodes#H_INVOKESPECIAL},
|
|
||||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or
|
|
||||||
* {@link Opcodes#H_INVOKEINTERFACE}.
|
|
||||||
* @param owner
|
|
||||||
* the internal name of the class that owns the field or method
|
|
||||||
* designated by this handle.
|
|
||||||
* @param name
|
|
||||||
* the name of the field or method designated by this handle.
|
|
||||||
* @param desc
|
|
||||||
* the descriptor of the field or method designated by this
|
|
||||||
* handle.
|
* handle.
|
||||||
*
|
* @param name the name of the field or method designated by this handle.
|
||||||
* @deprecated this constructor has been superseded
|
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||||
* by {@link #Handle(int, String, String, String, boolean)}.
|
* @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String,
|
||||||
|
* boolean)}.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Handle(int tag, String owner, String name, String desc) {
|
public Handle(final int tag, final String owner, final String name, final String descriptor) {
|
||||||
this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE);
|
this(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new field or method handle.
|
* Constructs a new field or method handle.
|
||||||
*
|
*
|
||||||
* @param tag
|
* @param tag the kind of field or method designated by this Handle. Must be {@link
|
||||||
* the kind of field or method designated by this Handle. Must be
|
* Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link
|
||||||
* {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
|
* Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
|
||||||
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
|
* {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link
|
||||||
* {@link Opcodes#H_INVOKEVIRTUAL},
|
* Opcodes#H_INVOKEINTERFACE}.
|
||||||
* {@link Opcodes#H_INVOKESTATIC},
|
* @param owner the internal name of the class that owns the field or method designated by this
|
||||||
* {@link Opcodes#H_INVOKESPECIAL},
|
|
||||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or
|
|
||||||
* {@link Opcodes#H_INVOKEINTERFACE}.
|
|
||||||
* @param owner
|
|
||||||
* the internal name of the class that owns the field or method
|
|
||||||
* designated by this handle.
|
|
||||||
* @param name
|
|
||||||
* the name of the field or method designated by this handle.
|
|
||||||
* @param desc
|
|
||||||
* the descriptor of the field or method designated by this
|
|
||||||
* handle.
|
* handle.
|
||||||
* @param itf
|
* @param name the name of the field or method designated by this handle.
|
||||||
* true if the owner is an interface.
|
* @param descriptor the descriptor of the field or method designated by this handle.
|
||||||
|
* @param isInterface whether the owner is an interface or not.
|
||||||
*/
|
*/
|
||||||
public Handle(int tag, String owner, String name, String desc, boolean itf) {
|
public Handle(
|
||||||
|
final int tag,
|
||||||
|
final String owner,
|
||||||
|
final String name,
|
||||||
|
final String descriptor,
|
||||||
|
final boolean isInterface) {
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.desc = desc;
|
this.descriptor = descriptor;
|
||||||
this.itf = itf;
|
this.isInterface = isInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the kind of field or method designated by this handle.
|
* Returns the kind of field or method designated by this handle.
|
||||||
*
|
*
|
||||||
* @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
|
* @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
|
||||||
* {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
|
* {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link
|
||||||
* {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
|
* Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link
|
||||||
* {@link Opcodes#H_INVOKESPECIAL},
|
* Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}.
|
||||||
* {@link Opcodes#H_NEWINVOKESPECIAL} or
|
|
||||||
* {@link Opcodes#H_INVOKEINTERFACE}.
|
|
||||||
*/
|
*/
|
||||||
public int getTag() {
|
public int getTag() {
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the internal name of the class that owns the field or method
|
* Returns the internal name of the class that owns the field or method designated by this handle.
|
||||||
* designated by this handle.
|
|
||||||
*
|
*
|
||||||
* @return the internal name of the class that owns the field or method
|
* @return the internal name of the class that owns the field or method designated by this handle.
|
||||||
* designated by this handle.
|
|
||||||
*/
|
*/
|
||||||
public String getOwner() {
|
public String getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
@ -199,53 +169,51 @@ public final class Handle {
|
|||||||
* @return the descriptor of the field or method designated by this handle.
|
* @return the descriptor of the field or method designated by this handle.
|
||||||
*/
|
*/
|
||||||
public String getDesc() {
|
public String getDesc() {
|
||||||
return desc;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the owner of the field or method designated
|
* Returns true if the owner of the field or method designated by this handle is an interface.
|
||||||
* by this handle is an interface.
|
|
||||||
*
|
*
|
||||||
* @return true if the owner of the field or method designated
|
* @return true if the owner of the field or method designated by this handle is an interface.
|
||||||
* by this handle is an interface.
|
|
||||||
*/
|
*/
|
||||||
public boolean isInterface() {
|
public boolean isInterface() {
|
||||||
return itf;
|
return isInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(final Object object) {
|
||||||
if (obj == this) {
|
if (object == this) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!(obj instanceof Handle)) {
|
if (!(object instanceof Handle)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Handle h = (Handle) obj;
|
Handle handle = (Handle) object;
|
||||||
return tag == h.tag && itf == h.itf && owner.equals(h.owner)
|
return tag == handle.tag
|
||||||
&& name.equals(h.name) && desc.equals(h.desc);
|
&& isInterface == handle.isInterface
|
||||||
|
&& owner.equals(handle.owner)
|
||||||
|
&& name.equals(handle.name)
|
||||||
|
&& descriptor.equals(handle.descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return tag + (itf? 64: 0) + owner.hashCode() * name.hashCode() * desc.hashCode();
|
return tag
|
||||||
|
+ (isInterface ? 64 : 0)
|
||||||
|
+ owner.hashCode() * name.hashCode() * descriptor.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the textual representation of this handle. The textual
|
* Returns the textual representation of this handle. The textual representation is:
|
||||||
* representation is:
|
|
||||||
*
|
*
|
||||||
* <pre>
|
* <ul>
|
||||||
* for a reference to a class:
|
* <li>for a reference to a class: owner "." name descriptor " (" tag ")",
|
||||||
* owner '.' name desc ' ' '(' tag ')'
|
* <li>for a reference to an interface: owner "." name descriptor " (" tag " itf)".
|
||||||
* for a reference to an interface:
|
* </ul>
|
||||||
* owner '.' name desc ' ' '(' tag ' ' itf ')'
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* . As this format is unambiguous, it can be parsed if necessary.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return owner + '.' + name + desc + " (" + tag + (itf? " itf": "") + ')';
|
return owner + '.' + name + descriptor + " (" + tag + (isInterface ? " itf" : "") + ')';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,92 +59,171 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about an exception handler block.
|
* Information about an exception handler. Corresponds to an element of the exception_table array of
|
||||||
|
* a Code attribute, as defined in the Java Virtual Machine Specification (JVMS). Handler instances
|
||||||
|
* can be chained together, with their {@link #nextHandler} field, to describe a full JVMS
|
||||||
|
* exception_table array.
|
||||||
*
|
*
|
||||||
|
* @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.3">JVMS
|
||||||
|
* 4.7.3</a>
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
*/
|
*/
|
||||||
class Handler {
|
final class Handler {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Beginning of the exception handler's scope (inclusive).
|
* The start_pc field of this JVMS exception_table entry. Corresponds to the beginning of the
|
||||||
|
* exception handler's scope (inclusive).
|
||||||
*/
|
*/
|
||||||
Label start;
|
final Label startPc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End of the exception handler's scope (exclusive).
|
* The end_pc field of this JVMS exception_table entry. Corresponds to the end of the exception
|
||||||
|
* handler's scope (exclusive).
|
||||||
*/
|
*/
|
||||||
Label end;
|
final Label endPc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Beginning of the exception handler's code.
|
* The handler_pc field of this JVMS exception_table entry. Corresponding to the beginning of the
|
||||||
|
* exception handler's code.
|
||||||
*/
|
*/
|
||||||
Label handler;
|
final Label handlerPc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal name of the type of exceptions handled by this handler, or
|
* The catch_type field of this JVMS exception_table entry. This is the constant pool index of the
|
||||||
* <tt>null</tt> to catch any exceptions.
|
* internal name of the type of exceptions handled by this handler, or 0 to catch any exceptions.
|
||||||
*/
|
*/
|
||||||
String desc;
|
final int catchType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant pool index of the internal name of the type of exceptions
|
* The internal name of the type of exceptions handled by this handler, or {@literal null} to
|
||||||
* handled by this handler, or 0 to catch any exceptions.
|
* catch any exceptions.
|
||||||
*/
|
*/
|
||||||
int type;
|
final String catchTypeDescriptor;
|
||||||
|
|
||||||
|
/** The next exception handler. */
|
||||||
|
Handler nextHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next exception handler block info.
|
* Constructs a new Handler.
|
||||||
*/
|
|
||||||
Handler next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the range between start and end from the given exception
|
|
||||||
* handlers.
|
|
||||||
*
|
*
|
||||||
* @param h
|
* @param startPc the start_pc field of this JVMS exception_table entry.
|
||||||
* an exception handler list.
|
* @param endPc the end_pc field of this JVMS exception_table entry.
|
||||||
* @param start
|
* @param handlerPc the handler_pc field of this JVMS exception_table entry.
|
||||||
* the start of the range to be removed.
|
* @param catchType The catch_type field of this JVMS exception_table entry.
|
||||||
* @param end
|
* @param catchTypeDescriptor The internal name of the type of exceptions handled by this handler,
|
||||||
* the end of the range to be removed. Maybe null.
|
* or {@literal null} to catch any exceptions.
|
||||||
|
*/
|
||||||
|
Handler(
|
||||||
|
final Label startPc,
|
||||||
|
final Label endPc,
|
||||||
|
final Label handlerPc,
|
||||||
|
final int catchType,
|
||||||
|
final String catchTypeDescriptor) {
|
||||||
|
this.startPc = startPc;
|
||||||
|
this.endPc = endPc;
|
||||||
|
this.handlerPc = handlerPc;
|
||||||
|
this.catchType = catchType;
|
||||||
|
this.catchTypeDescriptor = catchTypeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Handler from the given one, with a different scope.
|
||||||
|
*
|
||||||
|
* @param handler an existing Handler.
|
||||||
|
* @param startPc the start_pc field of this JVMS exception_table entry.
|
||||||
|
* @param endPc the end_pc field of this JVMS exception_table entry.
|
||||||
|
*/
|
||||||
|
Handler(final Handler handler, final Label startPc, final Label endPc) {
|
||||||
|
this(startPc, endPc, handler.handlerPc, handler.catchType, handler.catchTypeDescriptor);
|
||||||
|
this.nextHandler = handler.nextHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the range between start and end from the Handler list that begins with the given
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
||||||
|
* @param start the start of the range to be removed.
|
||||||
|
* @param end the end of the range to be removed. Maybe {@literal null}.
|
||||||
* @return the exception handler list with the start-end range removed.
|
* @return the exception handler list with the start-end range removed.
|
||||||
*/
|
*/
|
||||||
static Handler remove(Handler h, Label start, Label end) {
|
static Handler removeRange(final Handler firstHandler, final Label start, final Label end) {
|
||||||
if (h == null) {
|
if (firstHandler == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
h.next = remove(h.next, start, end);
|
firstHandler.nextHandler = removeRange(firstHandler.nextHandler, start, end);
|
||||||
}
|
}
|
||||||
int hstart = h.start.position;
|
int handlerStart = firstHandler.startPc.bytecodeOffset;
|
||||||
int hend = h.end.position;
|
int handlerEnd = firstHandler.endPc.bytecodeOffset;
|
||||||
int s = start.position;
|
int rangeStart = start.bytecodeOffset;
|
||||||
int e = end == null ? Integer.MAX_VALUE : end.position;
|
int rangeEnd = end == null ? Integer.MAX_VALUE : end.bytecodeOffset;
|
||||||
// if [hstart,hend[ and [s,e[ intervals intersect...
|
// Return early if [handlerStart,handlerEnd[ and [rangeStart,rangeEnd[ don't intersect.
|
||||||
if (s < hend && e > hstart) {
|
if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) {
|
||||||
if (s <= hstart) {
|
return firstHandler;
|
||||||
if (e >= hend) {
|
}
|
||||||
// [hstart,hend[ fully included in [s,e[, h removed
|
if (rangeStart <= handlerStart) {
|
||||||
h = h.next;
|
if (rangeEnd >= handlerEnd) {
|
||||||
|
// If [handlerStart,handlerEnd[ is included in [rangeStart,rangeEnd[, remove firstHandler.
|
||||||
|
return firstHandler.nextHandler;
|
||||||
} else {
|
} else {
|
||||||
// [hstart,hend[ minus [s,e[ = [e,hend[
|
// [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [rangeEnd,handlerEnd[
|
||||||
h.start = end;
|
return new Handler(firstHandler, end, firstHandler.endPc);
|
||||||
}
|
}
|
||||||
} else if (e >= hend) {
|
} else if (rangeEnd >= handlerEnd) {
|
||||||
// [hstart,hend[ minus [s,e[ = [hstart,s[
|
// [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [handlerStart,rangeStart[
|
||||||
h.end = start;
|
return new Handler(firstHandler, firstHandler.startPc, start);
|
||||||
} else {
|
} else {
|
||||||
// [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[
|
// [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ =
|
||||||
Handler g = new Handler();
|
// [handlerStart,rangeStart[ + [rangeEnd,handerEnd[
|
||||||
g.start = end;
|
firstHandler.nextHandler = new Handler(firstHandler, end, firstHandler.endPc);
|
||||||
g.end = h.end;
|
return new Handler(firstHandler, firstHandler.startPc, start);
|
||||||
g.handler = h.handler;
|
|
||||||
g.desc = h.desc;
|
|
||||||
g.type = h.type;
|
|
||||||
g.next = h.next;
|
|
||||||
h.end = start;
|
|
||||||
h.next = g;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return h;
|
|
||||||
|
/**
|
||||||
|
* Returns the number of elements of the Handler list that begins with the given element.
|
||||||
|
*
|
||||||
|
* @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
||||||
|
* @return the number of elements of the Handler list that begins with 'handler'.
|
||||||
|
*/
|
||||||
|
static int getExceptionTableLength(final Handler firstHandler) {
|
||||||
|
int length = 0;
|
||||||
|
Handler handler = firstHandler;
|
||||||
|
while (handler != null) {
|
||||||
|
length++;
|
||||||
|
handler = handler.nextHandler;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size in bytes of the JVMS exception_table corresponding to the Handler list that
|
||||||
|
* begins with the given element. <i>This includes the exception_table_length field.</i>
|
||||||
|
*
|
||||||
|
* @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
||||||
|
* @return the size in bytes of the exception_table_length and exception_table structures.
|
||||||
|
*/
|
||||||
|
static int getExceptionTableSize(final Handler firstHandler) {
|
||||||
|
return 2 + 8 * getExceptionTableLength(firstHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the JVMS exception_table corresponding to the Handler list that begins with the given
|
||||||
|
* element. <i>This includes the exception_table_length field.</i>
|
||||||
|
*
|
||||||
|
* @param firstHandler the beginning of a Handler list. May be {@literal null}.
|
||||||
|
* @param output where the exception_table_length and exception_table structures must be put.
|
||||||
|
*/
|
||||||
|
static void putExceptionTable(final Handler firstHandler, final ByteVector output) {
|
||||||
|
output.putShort(getExceptionTableLength(firstHandler));
|
||||||
|
Handler handler = firstHandler;
|
||||||
|
while (handler != null) {
|
||||||
|
output
|
||||||
|
.putShort(handler.startPc.bytecodeOffset)
|
||||||
|
.putShort(handler.endPc.bytecodeOffset)
|
||||||
|
.putShort(handler.handlerPc.bytecodeOffset)
|
||||||
|
.putShort(handler.catchType);
|
||||||
|
handler = handler.nextHandler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,347 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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. Oracle designates this
|
|
||||||
* particular file as subject to the "Classpath" exception as provided
|
|
||||||
* by Oracle in the LICENSE file that accompanied this code.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is available under and governed by the GNU General Public
|
|
||||||
* License version 2 only, as published by the Free Software Foundation.
|
|
||||||
* However, the following notice accompanied the original version of this
|
|
||||||
* file:
|
|
||||||
*
|
|
||||||
* ASM: a very small and fast Java bytecode manipulation framework
|
|
||||||
* Copyright (c) 2000-2011 INRIA, France Telecom
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the copyright holders nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
||||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
package jdk.internal.org.objectweb.asm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A constant pool item. Constant pool items can be created with the 'newXXX'
|
|
||||||
* methods in the {@link ClassWriter} class.
|
|
||||||
*
|
|
||||||
* @author Eric Bruneton
|
|
||||||
*/
|
|
||||||
final class Item {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Index of this item in the constant pool.
|
|
||||||
*/
|
|
||||||
int index;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of this constant pool item. A single class is used to represent all
|
|
||||||
* constant pool item types, in order to minimize the bytecode size of this
|
|
||||||
* package. The value of this field is one of {@link ClassWriter#INT},
|
|
||||||
* {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT},
|
|
||||||
* {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8},
|
|
||||||
* {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
|
|
||||||
* {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
|
|
||||||
* {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
|
|
||||||
* {@link ClassWriter#MODULE}, {@link ClassWriter#PACKAGE},
|
|
||||||
* {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
|
|
||||||
*
|
|
||||||
* MethodHandle constant 9 variations are stored using a range of 9 values
|
|
||||||
* from {@link ClassWriter#HANDLE_BASE} + 1 to
|
|
||||||
* {@link ClassWriter#HANDLE_BASE} + 9.
|
|
||||||
*
|
|
||||||
* Special Item types are used for Items that are stored in the ClassWriter
|
|
||||||
* {@link ClassWriter#typeTable}, instead of the constant pool, in order to
|
|
||||||
* avoid clashes with normal constant pool items in the ClassWriter constant
|
|
||||||
* pool's hash table. These special item types are
|
|
||||||
* {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and
|
|
||||||
* {@link ClassWriter#TYPE_MERGED}.
|
|
||||||
*/
|
|
||||||
int type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value of this item, for an integer item.
|
|
||||||
*/
|
|
||||||
int intVal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value of this item, for a long item.
|
|
||||||
*/
|
|
||||||
long longVal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* First part of the value of this item, for items that do not hold a
|
|
||||||
* primitive value.
|
|
||||||
*/
|
|
||||||
String strVal1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Second part of the value of this item, for items that do not hold a
|
|
||||||
* primitive value.
|
|
||||||
*/
|
|
||||||
String strVal2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Third part of the value of this item, for items that do not hold a
|
|
||||||
* primitive value.
|
|
||||||
*/
|
|
||||||
String strVal3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The hash code value of this constant pool item.
|
|
||||||
*/
|
|
||||||
int hashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Link to another constant pool item, used for collision lists in the
|
|
||||||
* constant pool's hash table.
|
|
||||||
*/
|
|
||||||
Item next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an uninitialized {@link Item}.
|
|
||||||
*/
|
|
||||||
Item() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an uninitialized {@link Item} for constant pool element at
|
|
||||||
* given position.
|
|
||||||
*
|
|
||||||
* @param index
|
|
||||||
* index of the item to be constructed.
|
|
||||||
*/
|
|
||||||
Item(final int index) {
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a copy of the given item.
|
|
||||||
*
|
|
||||||
* @param index
|
|
||||||
* index of the item to be constructed.
|
|
||||||
* @param i
|
|
||||||
* the item that must be copied into the item to be constructed.
|
|
||||||
*/
|
|
||||||
Item(final int index, final Item i) {
|
|
||||||
this.index = index;
|
|
||||||
type = i.type;
|
|
||||||
intVal = i.intVal;
|
|
||||||
longVal = i.longVal;
|
|
||||||
strVal1 = i.strVal1;
|
|
||||||
strVal2 = i.strVal2;
|
|
||||||
strVal3 = i.strVal3;
|
|
||||||
hashCode = i.hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this item to an integer item.
|
|
||||||
*
|
|
||||||
* @param intVal
|
|
||||||
* the value of this item.
|
|
||||||
*/
|
|
||||||
void set(final int intVal) {
|
|
||||||
this.type = ClassWriter.INT;
|
|
||||||
this.intVal = intVal;
|
|
||||||
this.hashCode = 0x7FFFFFFF & (type + intVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this item to a long item.
|
|
||||||
*
|
|
||||||
* @param longVal
|
|
||||||
* the value of this item.
|
|
||||||
*/
|
|
||||||
void set(final long longVal) {
|
|
||||||
this.type = ClassWriter.LONG;
|
|
||||||
this.longVal = longVal;
|
|
||||||
this.hashCode = 0x7FFFFFFF & (type + (int) longVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this item to a float item.
|
|
||||||
*
|
|
||||||
* @param floatVal
|
|
||||||
* the value of this item.
|
|
||||||
*/
|
|
||||||
void set(final float floatVal) {
|
|
||||||
this.type = ClassWriter.FLOAT;
|
|
||||||
this.intVal = Float.floatToRawIntBits(floatVal);
|
|
||||||
this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this item to a double item.
|
|
||||||
*
|
|
||||||
* @param doubleVal
|
|
||||||
* the value of this item.
|
|
||||||
*/
|
|
||||||
void set(final double doubleVal) {
|
|
||||||
this.type = ClassWriter.DOUBLE;
|
|
||||||
this.longVal = Double.doubleToRawLongBits(doubleVal);
|
|
||||||
this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets this item to an item that do not hold a primitive value.
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* the type of this item.
|
|
||||||
* @param strVal1
|
|
||||||
* first part of the value of this item.
|
|
||||||
* @param strVal2
|
|
||||||
* second part of the value of this item.
|
|
||||||
* @param strVal3
|
|
||||||
* third part of the value of this item.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("fallthrough")
|
|
||||||
void set(final int type, final String strVal1, final String strVal2,
|
|
||||||
final String strVal3) {
|
|
||||||
this.type = type;
|
|
||||||
this.strVal1 = strVal1;
|
|
||||||
this.strVal2 = strVal2;
|
|
||||||
this.strVal3 = strVal3;
|
|
||||||
switch (type) {
|
|
||||||
case ClassWriter.CLASS:
|
|
||||||
this.intVal = 0; // intVal of a class must be zero, see visitInnerClass
|
|
||||||
case ClassWriter.UTF8:
|
|
||||||
case ClassWriter.STR:
|
|
||||||
case ClassWriter.MTYPE:
|
|
||||||
case ClassWriter.MODULE:
|
|
||||||
case ClassWriter.PACKAGE:
|
|
||||||
case ClassWriter.TYPE_NORMAL:
|
|
||||||
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
|
|
||||||
return;
|
|
||||||
case ClassWriter.NAME_TYPE: {
|
|
||||||
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
|
|
||||||
* strVal2.hashCode());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ClassWriter.FIELD:
|
|
||||||
// ClassWriter.METH:
|
|
||||||
// ClassWriter.IMETH:
|
|
||||||
// ClassWriter.HANDLE_BASE + 1..9
|
|
||||||
default:
|
|
||||||
hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
|
|
||||||
* strVal2.hashCode() * strVal3.hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the item to an InvokeDynamic item.
|
|
||||||
*
|
|
||||||
* @param name
|
|
||||||
* invokedynamic's name.
|
|
||||||
* @param desc
|
|
||||||
* invokedynamic's desc.
|
|
||||||
* @param bsmIndex
|
|
||||||
* zero based index into the class attribute BootrapMethods.
|
|
||||||
*/
|
|
||||||
void set(String name, String desc, int bsmIndex) {
|
|
||||||
this.type = ClassWriter.INDY;
|
|
||||||
this.longVal = bsmIndex;
|
|
||||||
this.strVal1 = name;
|
|
||||||
this.strVal2 = desc;
|
|
||||||
this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex
|
|
||||||
* strVal1.hashCode() * strVal2.hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the item to a BootstrapMethod item.
|
|
||||||
*
|
|
||||||
* @param position
|
|
||||||
* position in byte in the class attribute BootrapMethods.
|
|
||||||
* @param hashCode
|
|
||||||
* hashcode of the item. This hashcode is processed from the
|
|
||||||
* hashcode of the bootstrap method and the hashcode of all
|
|
||||||
* bootstrap arguments.
|
|
||||||
*/
|
|
||||||
void set(int position, int hashCode) {
|
|
||||||
this.type = ClassWriter.BSM;
|
|
||||||
this.intVal = position;
|
|
||||||
this.hashCode = hashCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the given item is equal to this one. <i>This method assumes
|
|
||||||
* that the two items have the same {@link #type}</i>.
|
|
||||||
*
|
|
||||||
* @param i
|
|
||||||
* the item to be compared to this one. Both items must have the
|
|
||||||
* same {@link #type}.
|
|
||||||
* @return <tt>true</tt> if the given item if equal to this one,
|
|
||||||
* <tt>false</tt> otherwise.
|
|
||||||
*/
|
|
||||||
boolean isEqualTo(final Item i) {
|
|
||||||
switch (type) {
|
|
||||||
case ClassWriter.UTF8:
|
|
||||||
case ClassWriter.STR:
|
|
||||||
case ClassWriter.CLASS:
|
|
||||||
case ClassWriter.MODULE:
|
|
||||||
case ClassWriter.PACKAGE:
|
|
||||||
case ClassWriter.MTYPE:
|
|
||||||
case ClassWriter.TYPE_NORMAL:
|
|
||||||
return i.strVal1.equals(strVal1);
|
|
||||||
case ClassWriter.TYPE_MERGED:
|
|
||||||
case ClassWriter.LONG:
|
|
||||||
case ClassWriter.DOUBLE:
|
|
||||||
return i.longVal == longVal;
|
|
||||||
case ClassWriter.INT:
|
|
||||||
case ClassWriter.FLOAT:
|
|
||||||
return i.intVal == intVal;
|
|
||||||
case ClassWriter.TYPE_UNINIT:
|
|
||||||
return i.intVal == intVal && i.strVal1.equals(strVal1);
|
|
||||||
case ClassWriter.NAME_TYPE:
|
|
||||||
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2);
|
|
||||||
case ClassWriter.INDY: {
|
|
||||||
return i.longVal == longVal && i.strVal1.equals(strVal1)
|
|
||||||
&& i.strVal2.equals(strVal2);
|
|
||||||
}
|
|
||||||
// case ClassWriter.FIELD:
|
|
||||||
// case ClassWriter.METH:
|
|
||||||
// case ClassWriter.IMETH:
|
|
||||||
// case ClassWriter.HANDLE_BASE + 1..9
|
|
||||||
default:
|
|
||||||
return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2)
|
|
||||||
&& i.strVal3.equals(strVal3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -59,10 +59,9 @@
|
|||||||
package jdk.internal.org.objectweb.asm;
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A label represents a position in the bytecode of a method. Labels are used
|
* A position in the bytecode of a method. Labels are used for jump, goto, and switch instructions,
|
||||||
* for jump, goto, and switch instructions, and for try catch blocks. A label
|
* and for try catch blocks. A label designates the <i>instruction</i> that is just after. Note
|
||||||
* designates the <i>instruction</i> that is just after. Note however that there
|
* however that there can be other elements between a label and the instruction it designates (such
|
||||||
* can be other elements between a label and the instruction it designates (such
|
|
||||||
* as other labels, stack map frames, line numbers, etc.).
|
* as other labels, stack map frames, line numbers, etc.).
|
||||||
*
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
@ -70,516 +69,576 @@ package jdk.internal.org.objectweb.asm;
|
|||||||
public class Label {
|
public class Label {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this label is only used for debug attributes. Such a label
|
* A flag indicating that a label is only used for debug attributes. Such a label is not the start
|
||||||
* is not the start of a basic block, the target of a jump instruction, or
|
* of a basic block, the target of a jump instruction, or an exception handler. It can be safely
|
||||||
* an exception handler. It can be safely ignored in control flow graph
|
* ignored in control flow graph analysis algorithms (for optimization purposes).
|
||||||
* analysis algorithms (for optimization purposes).
|
|
||||||
*/
|
*/
|
||||||
static final int DEBUG = 1;
|
static final int FLAG_DEBUG_ONLY = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the position of this label is known.
|
* A flag indicating that a label is the target of a jump instruction, or the start of an
|
||||||
|
* exception handler.
|
||||||
*/
|
*/
|
||||||
static final int RESOLVED = 2;
|
static final int FLAG_JUMP_TARGET = 2;
|
||||||
|
|
||||||
|
/** A flag indicating that the bytecode offset of a label is known. */
|
||||||
|
static final int FLAG_RESOLVED = 4;
|
||||||
|
|
||||||
|
/** A flag indicating that a label corresponds to a reachable basic block. */
|
||||||
|
static final int FLAG_REACHABLE = 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this label has been updated, after instruction resizing.
|
* A flag indicating that the basic block corresponding to a label ends with a subroutine call. By
|
||||||
|
* construction in {@link MethodWriter#visitJumpInsn}, labels with this flag set have at least two
|
||||||
|
* outgoing edges:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>the first one corresponds to the instruction that follows the jsr instruction in the
|
||||||
|
* bytecode, i.e. where execution continues when it returns from the jsr call. This is a
|
||||||
|
* virtual control flow edge, since execution never goes directly from the jsr to the next
|
||||||
|
* instruction. Instead, it goes to the subroutine and eventually returns to the instruction
|
||||||
|
* following the jsr. This virtual edge is used to compute the real outgoing edges of the
|
||||||
|
* basic blocks ending with a ret instruction, in {@link #addSubroutineRetSuccessors}.
|
||||||
|
* <li>the second one corresponds to the target of the jsr instruction,
|
||||||
|
* </ul>
|
||||||
*/
|
*/
|
||||||
static final int RESIZED = 4;
|
static final int FLAG_SUBROUTINE_CALLER = 16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this basic block has been pushed in the basic block stack.
|
* A flag indicating that the basic block corresponding to a label is the start of a subroutine.
|
||||||
* See {@link MethodWriter#visitMaxs visitMaxs}.
|
|
||||||
*/
|
*/
|
||||||
static final int PUSHED = 8;
|
static final int FLAG_SUBROUTINE_START = 32;
|
||||||
|
|
||||||
|
/** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */
|
||||||
|
static final int FLAG_SUBROUTINE_END = 64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this label is the target of a jump instruction, or the start
|
* The number of elements to add to the {@link #otherLineNumbers} array when it needs to be
|
||||||
* of an exception handler.
|
* resized to store a new source line number.
|
||||||
*/
|
*/
|
||||||
static final int TARGET = 16;
|
static final int LINE_NUMBERS_CAPACITY_INCREMENT = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if a stack map frame must be stored for this label.
|
* The number of elements to add to the {@link #forwardReferences} array when it needs to be
|
||||||
|
* resized to store a new forward reference.
|
||||||
*/
|
*/
|
||||||
static final int STORE = 32;
|
static final int FORWARD_REFERENCES_CAPACITY_INCREMENT = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this label corresponds to a reachable basic block.
|
* The bit mask to extract the type of a forward reference to this label. The extracted type is
|
||||||
|
* either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}.
|
||||||
|
*
|
||||||
|
* @see #forwardReferences
|
||||||
*/
|
*/
|
||||||
static final int REACHABLE = 64;
|
static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this basic block ends with a JSR instruction.
|
* The type of forward references stored with two bytes in the bytecode. This is the case, for
|
||||||
|
* instance, of a forward reference from an ifnull instruction.
|
||||||
*/
|
*/
|
||||||
static final int JSR = 128;
|
static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this basic block ends with a RET instruction.
|
* The type of forward references stored in four bytes in the bytecode. This is the case, for
|
||||||
|
* instance, of a forward reference from a lookupswitch instruction.
|
||||||
*/
|
*/
|
||||||
static final int RET = 256;
|
static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this basic block is the start of a subroutine.
|
* The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle
|
||||||
|
* is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes,
|
||||||
|
* as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}).
|
||||||
|
*
|
||||||
|
* @see #forwardReferences
|
||||||
*/
|
*/
|
||||||
static final int SUBROUTINE = 512;
|
static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this subroutine basic block has been visited by a
|
* A sentinel element used to indicate the end of a list of labels.
|
||||||
* visitSubroutine(null, ...) call.
|
*
|
||||||
|
* @see #nextListElement
|
||||||
*/
|
*/
|
||||||
static final int VISITED = 1024;
|
static final Label EMPTY_LIST = new Label();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this subroutine basic block has been visited by a
|
* A user managed state associated with this label. Warning: this field is used by the ASM tree
|
||||||
* visitSubroutine(!null, ...) call.
|
* package. In order to use it with the ASM tree package you must override the getLabelNode method
|
||||||
*/
|
* in MethodNode.
|
||||||
static final int VISITED2 = 2048;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Field used to associate user information to a label. Warning: this field
|
|
||||||
* is used by the ASM tree package. In order to use it with the ASM tree
|
|
||||||
* package you must override the
|
|
||||||
* {@link jdk.internal.org.objectweb.asm.tree.MethodNode#getLabelNode} method.
|
|
||||||
*/
|
*/
|
||||||
public Object info;
|
public Object info;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags that indicate the status of this label.
|
* The type and status of this label or its corresponding basic block. Must be zero or more of
|
||||||
|
* {@link #FLAG_DEBUG_ONLY}, {@link #FLAG_JUMP_TARGET}, {@link #FLAG_RESOLVED}, {@link
|
||||||
|
* #FLAG_REACHABLE}, {@link #FLAG_SUBROUTINE_CALLER}, {@link #FLAG_SUBROUTINE_START}, {@link
|
||||||
|
* #FLAG_SUBROUTINE_END}.
|
||||||
|
*/
|
||||||
|
short flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source line number corresponding to this label, or 0. If there are several source line
|
||||||
|
* numbers corresponding to this label, the first one is stored in this field, and the remaining
|
||||||
|
* ones are stored in {@link #otherLineNumbers}.
|
||||||
|
*/
|
||||||
|
private short lineNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source line numbers corresponding to this label, in addition to {@link #lineNumber}, or
|
||||||
|
* null. The first element of this array is the number n of source line numbers it contains, which
|
||||||
|
* are stored between indices 1 and n (inclusive).
|
||||||
|
*/
|
||||||
|
private int[] otherLineNumbers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset of this label in the bytecode of its method, in bytes. This value is set if and only
|
||||||
|
* if the {@link #FLAG_RESOLVED} flag is set.
|
||||||
|
*/
|
||||||
|
int bytecodeOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The forward references to this label. The first element is the number of forward references,
|
||||||
|
* times 2 (this corresponds to the index of the last element actually used in this array). Then,
|
||||||
|
* each forward reference is described with two consecutive integers noted
|
||||||
|
* 'sourceInsnBytecodeOffset' and 'reference':
|
||||||
*
|
*
|
||||||
* @see #DEBUG
|
* <ul>
|
||||||
* @see #RESOLVED
|
* <li>'sourceInsnBytecodeOffset' is the bytecode offset of the instruction that contains the
|
||||||
* @see #RESIZED
|
* forward reference,
|
||||||
* @see #PUSHED
|
* <li>'reference' contains the type and the offset in the bytecode where the forward reference
|
||||||
* @see #TARGET
|
* value must be stored, which can be extracted with {@link #FORWARD_REFERENCE_TYPE_MASK}
|
||||||
* @see #STORE
|
* and {@link #FORWARD_REFERENCE_HANDLE_MASK}.
|
||||||
* @see #REACHABLE
|
* </ul>
|
||||||
* @see #JSR
|
|
||||||
* @see #RET
|
|
||||||
*/
|
|
||||||
int status;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The line number corresponding to this label, if known. If there are
|
|
||||||
* several lines, each line is stored in a separate label, all linked via
|
|
||||||
* their next field (these links are created in ClassReader and removed just
|
|
||||||
* before visitLabel is called, so that this does not impact the rest of the
|
|
||||||
* code).
|
|
||||||
*/
|
|
||||||
int line;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The position of this label in the code, if known.
|
|
||||||
*/
|
|
||||||
int position;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of forward references to this label, times two.
|
|
||||||
*/
|
|
||||||
private int referenceCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Informations about forward references. Each forward reference is
|
|
||||||
* described by two consecutive integers in this array: the first one is the
|
|
||||||
* position of the first byte of the bytecode instruction that contains the
|
|
||||||
* forward reference, while the second is the position of the first byte of
|
|
||||||
* the forward reference itself. In fact the sign of the first integer
|
|
||||||
* indicates if this reference uses 2 or 4 bytes, and its absolute value
|
|
||||||
* gives the position of the bytecode instruction. This array is also used
|
|
||||||
* as a bitset to store the subroutines to which a basic block belongs. This
|
|
||||||
* information is needed in {@linked MethodWriter#visitMaxs}, after all
|
|
||||||
* forward references have been resolved. Hence the same array can be used
|
|
||||||
* for both purposes without problems.
|
|
||||||
*/
|
|
||||||
private int[] srcAndRefPositions;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fields for the control flow and data flow graph analysis algorithms (used
|
|
||||||
* to compute the maximum stack size or the stack map frames). A control
|
|
||||||
* flow graph contains one node per "basic block", and one edge per "jump"
|
|
||||||
* from one basic block to another. Each node (i.e., each basic block) is
|
|
||||||
* represented by the Label object that corresponds to the first instruction
|
|
||||||
* of this basic block. Each node also stores the list of its successors in
|
|
||||||
* the graph, as a linked list of Edge objects.
|
|
||||||
*
|
*
|
||||||
* The control flow analysis algorithms used to compute the maximum stack
|
* <p>For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is
|
||||||
* size or the stack map frames are similar and use two steps. The first
|
* equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1
|
||||||
* step, during the visit of each instruction, builds information about the
|
* (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after
|
||||||
* state of the local variables and the operand stack at the end of each
|
* the start of the instruction itself). For the default case of a lookupswitch instruction at
|
||||||
* basic block, called the "output frame", <i>relatively</i> to the frame
|
* bytecode offset x, 'sourceInsnBytecodeOffset' is equal to x, and 'reference' is of type {@link
|
||||||
* state at the beginning of the basic block, which is called the "input
|
* #FORWARD_REFERENCE_TYPE_WIDE} with value between x + 1 and x + 4 (because the lookupswitch
|
||||||
* frame", and which is <i>unknown</i> during this step. The second step, in
|
* instruction uses a 4 bytes bytecode offset operand stored one to four bytes after the start of
|
||||||
* {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes
|
* the instruction itself).
|
||||||
* information about the input frame of each basic block, from the input
|
|
||||||
* state of the first basic block (known from the method signature), and by
|
|
||||||
* the using the previously computed relative output frames.
|
|
||||||
*
|
|
||||||
* The algorithm used to compute the maximum stack size only computes the
|
|
||||||
* relative output and absolute input stack heights, while the algorithm
|
|
||||||
* used to compute stack map frames computes relative output frames and
|
|
||||||
* absolute input frames.
|
|
||||||
*/
|
*/
|
||||||
|
private int[] forwardReferences;
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Fields for the control flow and data flow graph analysis algorithms (used to compute the
|
||||||
|
// maximum stack size or the stack map frames). A control flow graph contains one node per "basic
|
||||||
|
// block", and one edge per "jump" from one basic block to another. Each node (i.e., each basic
|
||||||
|
// block) is represented with the Label object that corresponds to the first instruction of this
|
||||||
|
// basic block. Each node also stores the list of its successors in the graph, as a linked list of
|
||||||
|
// Edge objects.
|
||||||
|
//
|
||||||
|
// The control flow analysis algorithms used to compute the maximum stack size or the stack map
|
||||||
|
// frames are similar and use two steps. The first step, during the visit of each instruction,
|
||||||
|
// builds information about the state of the local variables and the operand stack at the end of
|
||||||
|
// each basic block, called the "output frame", <i>relatively</i> to the frame state at the
|
||||||
|
// beginning of the basic block, which is called the "input frame", and which is <i>unknown</i>
|
||||||
|
// during this step. The second step, in {@link MethodWriter#computeAllFrames} and {@link
|
||||||
|
// MethodWriter#computeMaxStackAndLocal}, is a fix point algorithm
|
||||||
|
// that computes information about the input frame of each basic block, from the input state of
|
||||||
|
// the first basic block (known from the method signature), and by the using the previously
|
||||||
|
// computed relative output frames.
|
||||||
|
//
|
||||||
|
// The algorithm used to compute the maximum stack size only computes the relative output and
|
||||||
|
// absolute input stack heights, while the algorithm used to compute stack map frames computes
|
||||||
|
// relative output frames and absolute input frames.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start of the output stack relatively to the input stack. The exact
|
* The number of elements in the input stack of the basic block corresponding to this label. This
|
||||||
* semantics of this field depends on the algorithm that is used.
|
* field is computed in {@link MethodWriter#computeMaxStackAndLocal}.
|
||||||
*
|
|
||||||
* When only the maximum stack size is computed, this field is the number of
|
|
||||||
* elements in the input stack.
|
|
||||||
*
|
|
||||||
* When the stack map frames are completely computed, this field is the
|
|
||||||
* offset of the first output stack element relatively to the top of the
|
|
||||||
* input stack. This offset is always negative or null. A null offset means
|
|
||||||
* that the output stack must be appended to the input stack. A -n offset
|
|
||||||
* means that the first n output stack elements must replace the top n input
|
|
||||||
* stack elements, and that the other elements must be appended to the input
|
|
||||||
* stack.
|
|
||||||
*/
|
*/
|
||||||
int inputStackTop;
|
short inputStackSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum height reached by the output stack, relatively to the top of the
|
* The number of elements in the output stack, at the end of the basic block corresponding to this
|
||||||
* input stack. This maximum is always positive or null.
|
* label. This field is only computed for basic blocks that end with a RET instruction.
|
||||||
*/
|
*/
|
||||||
int outputStackMax;
|
short outputStackSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about the input and output stack map frames of this basic
|
* The maximum height reached by the output stack, relatively to the top of the input stack, in
|
||||||
* block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
|
* the basic block corresponding to this label. This maximum is always positive or null.
|
||||||
* option is used.
|
*/
|
||||||
|
short outputStackMax;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the subroutine to which this basic block belongs, or 0. If the basic block belongs to
|
||||||
|
* several subroutines, this is the id of the "oldest" subroutine that contains it (with the
|
||||||
|
* convention that a subroutine calling another one is "older" than the callee). This field is
|
||||||
|
* computed in {@link MethodWriter#computeMaxStackAndLocal}, if the method contains JSR
|
||||||
|
* instructions.
|
||||||
|
*/
|
||||||
|
short subroutineId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The input and output stack map frames of the basic block corresponding to this label. This
|
||||||
|
* field is only used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} or {@link
|
||||||
|
* MethodWriter#COMPUTE_INSERTED_FRAMES} option is used.
|
||||||
*/
|
*/
|
||||||
Frame frame;
|
Frame frame;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The successor of this label, in the order they are visited. This linked
|
* The successor of this label, in the order they are visited in {@link MethodVisitor#visitLabel}.
|
||||||
* list does not include labels used for debug info only. If
|
* This linked list does not include labels used for debug info only. If the {@link
|
||||||
* {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
|
* MethodWriter#COMPUTE_ALL_FRAMES} or {@link MethodWriter#COMPUTE_INSERTED_FRAMES} option is used
|
||||||
* does not contain successive labels that denote the same bytecode position
|
* then it does not contain either successive labels that denote the same bytecode offset (in this
|
||||||
* (in this case only the first label appears in this list).
|
* case only the first label appears in this list).
|
||||||
*/
|
*/
|
||||||
Label successor;
|
Label nextBasicBlock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The successors of this node in the control flow graph. These successors
|
* The outgoing edges of the basic block corresponding to this label, in the control flow graph of
|
||||||
* are stored in a linked list of {@link Edge Edge} objects, linked to each
|
* its method. These edges are stored in a linked list of {@link Edge} objects, linked to each
|
||||||
* other by their {@link Edge#next} field.
|
* other by their {@link Edge#nextEdge} field.
|
||||||
*/
|
*/
|
||||||
Edge successors;
|
Edge outgoingEdges;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The next basic block in the basic block stack. This stack is used in the
|
* The next element in the list of labels to which this label belongs, or null if it does not
|
||||||
* main loop of the fix point algorithm used in the second step of the
|
* belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in
|
||||||
* control flow analysis algorithms. It is also used in
|
* order to ensure that this field is null if and only if this label does not belong to a list of
|
||||||
* {@link #visitSubroutine} to avoid using a recursive method, and in
|
* labels. Note that there can be several lists of labels at the same time, but that a label can
|
||||||
* ClassReader to temporarily store multiple source lines for a label.
|
* belong to at most one list at a time (unless some lists share a common tail, but this is not
|
||||||
|
* used in practice).
|
||||||
*
|
*
|
||||||
* @see MethodWriter#visitMaxs
|
* <p>List of labels are used in {@link MethodWriter#computeAllFrames} and {@link
|
||||||
|
* MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size,
|
||||||
|
* respectively, as well as in {@link #markSubroutine} and {@link #addSubroutineRetSuccessors} to
|
||||||
|
* compute the basic blocks belonging to subroutines and their outgoing edges. Outside of these
|
||||||
|
* methods, this field should be null (this property is a precondition and a postcondition of
|
||||||
|
* these methods).
|
||||||
*/
|
*/
|
||||||
Label next;
|
Label nextListElement;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Constructor
|
// Constructor and accessors
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/** Constructs a new label. */
|
||||||
* Constructs a new label.
|
|
||||||
*/
|
|
||||||
public Label() {
|
public Label() {
|
||||||
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Methods to compute offsets and to manage forward references
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the offset corresponding to this label. This offset is computed
|
* Returns the bytecode offset corresponding to this label. This offset is computed from the start
|
||||||
* from the start of the method's bytecode. <i>This method is intended for
|
* of the method's bytecode. <i>This method is intended for {@link Attribute} sub classes, and is
|
||||||
* {@link Attribute} sub classes, and is normally not needed by class
|
* normally not needed by class generators or adapters.</i>
|
||||||
* generators or adapters.</i>
|
|
||||||
*
|
*
|
||||||
* @return the offset corresponding to this label.
|
* @return the bytecode offset corresponding to this label.
|
||||||
* @throws IllegalStateException
|
* @throws IllegalStateException if this label is not resolved yet.
|
||||||
* if this label is not resolved yet.
|
|
||||||
*/
|
*/
|
||||||
public int getOffset() {
|
public int getOffset() {
|
||||||
if ((status & RESOLVED) == 0) {
|
if ((flags & FLAG_RESOLVED) == 0) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("Label offset position has not been resolved yet");
|
||||||
"Label offset position has not been resolved yet");
|
|
||||||
}
|
}
|
||||||
return position;
|
return bytecodeOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts a reference to this label in the bytecode of a method. If the
|
* Returns the "canonical" {@link Label} instance corresponding to this label's bytecode offset,
|
||||||
* position of the label is known, the offset is computed and written
|
* if known, otherwise the label itself. The canonical instance is the first label (in the order
|
||||||
* directly. Otherwise, a null offset is written and a new forward reference
|
* of their visit by {@link MethodVisitor#visitLabel}) corresponding to this bytecode offset. It
|
||||||
* is declared for this label.
|
* cannot be known for labels which have not been visited yet.
|
||||||
*
|
*
|
||||||
* @param owner
|
* <p><i>This method should only be used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} option
|
||||||
* the code writer that calls this method.
|
* is used.</i>
|
||||||
* @param out
|
|
||||||
* the bytecode of the method.
|
|
||||||
* @param source
|
|
||||||
* the position of first byte of the bytecode instruction that
|
|
||||||
* contains this label.
|
|
||||||
* @param wideOffset
|
|
||||||
* <tt>true</tt> if the reference must be stored in 4 bytes, or
|
|
||||||
* <tt>false</tt> if it must be stored with 2 bytes.
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if this label has not been created by the given code writer.
|
|
||||||
*/
|
|
||||||
void put(final MethodWriter owner, final ByteVector out, final int source,
|
|
||||||
final boolean wideOffset) {
|
|
||||||
if ((status & RESOLVED) == 0) {
|
|
||||||
if (wideOffset) {
|
|
||||||
addReference(-1 - source, out.length);
|
|
||||||
out.putInt(-1);
|
|
||||||
} else {
|
|
||||||
addReference(source, out.length);
|
|
||||||
out.putShort(-1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (wideOffset) {
|
|
||||||
out.putInt(position - source);
|
|
||||||
} else {
|
|
||||||
out.putShort(position - source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a forward reference to this label. This method must be called only
|
|
||||||
* for a true forward reference, i.e. only if this label is not resolved
|
|
||||||
* yet. For backward references, the offset of the reference can be, and
|
|
||||||
* must be, computed and stored directly.
|
|
||||||
*
|
*
|
||||||
* @param sourcePosition
|
* @return the label itself if {@link #frame} is null, otherwise the Label's frame owner. This
|
||||||
* the position of the referencing instruction. This position
|
* corresponds to the "canonical" label instance described above thanks to the way the label
|
||||||
* will be used to compute the offset of this forward reference.
|
* frame is set in {@link MethodWriter#visitLabel}.
|
||||||
* @param referencePosition
|
|
||||||
* the position where the offset for this forward reference must
|
|
||||||
* be stored.
|
|
||||||
*/
|
*/
|
||||||
private void addReference(final int sourcePosition,
|
final Label getCanonicalInstance() {
|
||||||
final int referencePosition) {
|
|
||||||
if (srcAndRefPositions == null) {
|
|
||||||
srcAndRefPositions = new int[6];
|
|
||||||
}
|
|
||||||
if (referenceCount >= srcAndRefPositions.length) {
|
|
||||||
int[] a = new int[srcAndRefPositions.length + 6];
|
|
||||||
System.arraycopy(srcAndRefPositions, 0, a, 0,
|
|
||||||
srcAndRefPositions.length);
|
|
||||||
srcAndRefPositions = a;
|
|
||||||
}
|
|
||||||
srcAndRefPositions[referenceCount++] = sourcePosition;
|
|
||||||
srcAndRefPositions[referenceCount++] = referencePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves all forward references to this label. This method must be called
|
|
||||||
* when this label is added to the bytecode of the method, i.e. when its
|
|
||||||
* position becomes known. This method fills in the blanks that where left
|
|
||||||
* in the bytecode by each forward reference previously added to this label.
|
|
||||||
*
|
|
||||||
* @param owner
|
|
||||||
* the code writer that calls this method.
|
|
||||||
* @param position
|
|
||||||
* the position of this label in the bytecode.
|
|
||||||
* @param data
|
|
||||||
* the bytecode of the method.
|
|
||||||
* @return <tt>true</tt> if a blank that was left for this label was too
|
|
||||||
* small to store the offset. In such a case the corresponding jump
|
|
||||||
* instruction is replaced with a pseudo instruction (using unused
|
|
||||||
* opcodes) using an unsigned two bytes offset. These pseudo
|
|
||||||
* instructions will be replaced with standard bytecode instructions
|
|
||||||
* with wider offsets (4 bytes instead of 2), in ClassReader.
|
|
||||||
* @throws IllegalArgumentException
|
|
||||||
* if this label has already been resolved, or if it has not
|
|
||||||
* been created by the given code writer.
|
|
||||||
*/
|
|
||||||
boolean resolve(final MethodWriter owner, final int position,
|
|
||||||
final byte[] data) {
|
|
||||||
boolean needUpdate = false;
|
|
||||||
this.status |= RESOLVED;
|
|
||||||
this.position = position;
|
|
||||||
int i = 0;
|
|
||||||
while (i < referenceCount) {
|
|
||||||
int source = srcAndRefPositions[i++];
|
|
||||||
int reference = srcAndRefPositions[i++];
|
|
||||||
int offset;
|
|
||||||
if (source >= 0) {
|
|
||||||
offset = position - source;
|
|
||||||
if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
|
|
||||||
/*
|
|
||||||
* changes the opcode of the jump instruction, in order to
|
|
||||||
* be able to find it later (see resizeInstructions in
|
|
||||||
* MethodWriter). These temporary opcodes are similar to
|
|
||||||
* jump instruction opcodes, except that the 2 bytes offset
|
|
||||||
* is unsigned (and can therefore represent values from 0 to
|
|
||||||
* 65535, which is sufficient since the size of a method is
|
|
||||||
* limited to 65535 bytes).
|
|
||||||
*/
|
|
||||||
int opcode = data[reference - 1] & 0xFF;
|
|
||||||
if (opcode <= Opcodes.JSR) {
|
|
||||||
// changes IFEQ ... JSR to opcodes 202 to 217
|
|
||||||
data[reference - 1] = (byte) (opcode + 49);
|
|
||||||
} else {
|
|
||||||
// changes IFNULL and IFNONNULL to opcodes 218 and 219
|
|
||||||
data[reference - 1] = (byte) (opcode + 20);
|
|
||||||
}
|
|
||||||
needUpdate = true;
|
|
||||||
}
|
|
||||||
data[reference++] = (byte) (offset >>> 8);
|
|
||||||
data[reference] = (byte) offset;
|
|
||||||
} else {
|
|
||||||
offset = position + source + 1;
|
|
||||||
data[reference++] = (byte) (offset >>> 24);
|
|
||||||
data[reference++] = (byte) (offset >>> 16);
|
|
||||||
data[reference++] = (byte) (offset >>> 8);
|
|
||||||
data[reference] = (byte) offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return needUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first label of the series to which this label belongs. For an
|
|
||||||
* isolated label or for the first label in a series of successive labels,
|
|
||||||
* this method returns the label itself. For other labels it returns the
|
|
||||||
* first label of the series.
|
|
||||||
*
|
|
||||||
* @return the first label of the series to which this label belongs.
|
|
||||||
*/
|
|
||||||
Label getFirst() {
|
|
||||||
return frame == null ? this : frame.owner;
|
return frame == null ? this : frame.owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
// Methods related to subroutines
|
// Methods to manage line numbers
|
||||||
// ------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true is this basic block belongs to the given subroutine.
|
* Adds a source line number corresponding to this label.
|
||||||
*
|
*
|
||||||
* @param id
|
* @param lineNumber a source line number (which should be strictly positive).
|
||||||
* a subroutine id.
|
|
||||||
* @return true is this basic block belongs to the given subroutine.
|
|
||||||
*/
|
*/
|
||||||
boolean inSubroutine(final long id) {
|
final void addLineNumber(final int lineNumber) {
|
||||||
if ((status & Label.VISITED) != 0) {
|
if (this.lineNumber == 0) {
|
||||||
return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0;
|
this.lineNumber = (short) lineNumber;
|
||||||
|
} else {
|
||||||
|
if (otherLineNumbers == null) {
|
||||||
|
otherLineNumbers = new int[LINE_NUMBERS_CAPACITY_INCREMENT];
|
||||||
|
}
|
||||||
|
int otherLineNumberIndex = ++otherLineNumbers[0];
|
||||||
|
if (otherLineNumberIndex >= otherLineNumbers.length) {
|
||||||
|
int[] newLineNumbers = new int[otherLineNumbers.length + LINE_NUMBERS_CAPACITY_INCREMENT];
|
||||||
|
System.arraycopy(otherLineNumbers, 0, newLineNumbers, 0, otherLineNumbers.length);
|
||||||
|
otherLineNumbers = newLineNumbers;
|
||||||
|
}
|
||||||
|
otherLineNumbers[otherLineNumberIndex] = lineNumber;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this basic block and the given one belong to a common
|
* Makes the given visitor visit this label and its source line numbers, if applicable.
|
||||||
* subroutine.
|
|
||||||
*
|
*
|
||||||
* @param block
|
* @param methodVisitor a method visitor.
|
||||||
* another basic block.
|
* @param visitLineNumbers whether to visit of the label's source line numbers, if any.
|
||||||
* @return true if this basic block and the given one belong to a common
|
|
||||||
* subroutine.
|
|
||||||
*/
|
*/
|
||||||
boolean inSameSubroutine(final Label block) {
|
final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) {
|
||||||
if ((status & VISITED) == 0 || (block.status & VISITED) == 0) {
|
methodVisitor.visitLabel(this);
|
||||||
return false;
|
if (visitLineNumbers && lineNumber != 0) {
|
||||||
}
|
methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this);
|
||||||
for (int i = 0; i < srcAndRefPositions.length; ++i) {
|
if (otherLineNumbers != null) {
|
||||||
if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) {
|
for (int i = 1; i <= otherLineNumbers[0]; ++i) {
|
||||||
return true;
|
methodVisitor.visitLineNumber(otherLineNumbers[i], this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
// Methods to compute offsets and to manage forward references
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this basic block as belonging to the given subroutine.
|
* Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label
|
||||||
|
* is known, the relative bytecode offset between the label and the instruction referencing it is
|
||||||
|
* computed and written directly. Otherwise, a null relative offset is written and a new forward
|
||||||
|
* reference is declared for this label.
|
||||||
*
|
*
|
||||||
* @param id
|
* @param code the bytecode of the method. This is where the reference is appended.
|
||||||
* a subroutine id.
|
* @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
|
||||||
* @param nbSubroutines
|
* reference to be appended.
|
||||||
* the total number of subroutines in the method.
|
* @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes).
|
||||||
*/
|
*/
|
||||||
void addToSubroutine(final long id, final int nbSubroutines) {
|
final void put(
|
||||||
if ((status & VISITED) == 0) {
|
final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) {
|
||||||
status |= VISITED;
|
if ((flags & FLAG_RESOLVED) == 0) {
|
||||||
srcAndRefPositions = new int[nbSubroutines / 32 + 1];
|
if (wideReference) {
|
||||||
}
|
addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length);
|
||||||
srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
|
code.putInt(-1);
|
||||||
}
|
} else {
|
||||||
|
addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length);
|
||||||
/**
|
code.putShort(-1);
|
||||||
* Finds the basic blocks that belong to a given subroutine, and marks these
|
|
||||||
* blocks as belonging to this subroutine. This method follows the control
|
|
||||||
* flow graph to find all the blocks that are reachable from the current
|
|
||||||
* block WITHOUT following any JSR target.
|
|
||||||
*
|
|
||||||
* @param JSR
|
|
||||||
* a JSR block that jumps to this subroutine. If this JSR is not
|
|
||||||
* null it is added to the successor of the RET blocks found in
|
|
||||||
* the subroutine.
|
|
||||||
* @param id
|
|
||||||
* the id of this subroutine.
|
|
||||||
* @param nbSubroutines
|
|
||||||
* the total number of subroutines in the method.
|
|
||||||
*/
|
|
||||||
void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) {
|
|
||||||
// user managed stack of labels, to avoid using a recursive method
|
|
||||||
// (recursivity can lead to stack overflow with very large methods)
|
|
||||||
Label stack = this;
|
|
||||||
while (stack != null) {
|
|
||||||
// removes a label l from the stack
|
|
||||||
Label l = stack;
|
|
||||||
stack = l.next;
|
|
||||||
l.next = null;
|
|
||||||
|
|
||||||
if (JSR != null) {
|
|
||||||
if ((l.status & VISITED2) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
l.status |= VISITED2;
|
|
||||||
// adds JSR to the successors of l, if it is a RET block
|
|
||||||
if ((l.status & RET) != 0) {
|
|
||||||
if (!l.inSameSubroutine(JSR)) {
|
|
||||||
Edge e = new Edge();
|
|
||||||
e.info = l.inputStackTop;
|
|
||||||
e.successor = JSR.successors.successor;
|
|
||||||
e.next = l.successors;
|
|
||||||
l.successors = e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if the l block already belongs to subroutine 'id', continue
|
if (wideReference) {
|
||||||
if (l.inSubroutine(id)) {
|
code.putInt(bytecodeOffset - sourceInsnBytecodeOffset);
|
||||||
continue;
|
} else {
|
||||||
}
|
code.putShort(bytecodeOffset - sourceInsnBytecodeOffset);
|
||||||
// marks the l block as belonging to subroutine 'id'
|
|
||||||
l.addToSubroutine(id, nbSubroutines);
|
|
||||||
}
|
|
||||||
// pushes each successor of l on the stack, except JSR targets
|
|
||||||
Edge e = l.successors;
|
|
||||||
while (e != null) {
|
|
||||||
// if the l block is a JSR block, then 'l.successors.next' leads
|
|
||||||
// to the JSR target (see {@link #visitJumpInsn}) and must
|
|
||||||
// therefore not be followed
|
|
||||||
if ((l.status & Label.JSR) == 0 || e != l.successors.next) {
|
|
||||||
// pushes e.successor on the stack if it not already added
|
|
||||||
if (e.successor.next == null) {
|
|
||||||
e.successor.next = stack;
|
|
||||||
stack = e.successor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
e = e.next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
/**
|
||||||
// Overriden Object methods
|
* Adds a forward reference to this label. This method must be called only for a true forward
|
||||||
// ------------------------------------------------------------------------
|
* reference, i.e. only if this label is not resolved yet. For backward references, the relative
|
||||||
|
* bytecode offset of the reference can be, and must be, computed and stored directly.
|
||||||
|
*
|
||||||
|
* @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the
|
||||||
|
* reference stored at referenceHandle.
|
||||||
|
* @param referenceType either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link
|
||||||
|
* #FORWARD_REFERENCE_TYPE_WIDE}.
|
||||||
|
* @param referenceHandle the offset in the bytecode where the forward reference value must be
|
||||||
|
* stored.
|
||||||
|
*/
|
||||||
|
private void addForwardReference(
|
||||||
|
final int sourceInsnBytecodeOffset, final int referenceType, final int referenceHandle) {
|
||||||
|
if (forwardReferences == null) {
|
||||||
|
forwardReferences = new int[FORWARD_REFERENCES_CAPACITY_INCREMENT];
|
||||||
|
}
|
||||||
|
int lastElementIndex = forwardReferences[0];
|
||||||
|
if (lastElementIndex + 2 >= forwardReferences.length) {
|
||||||
|
int[] newValues = new int[forwardReferences.length + FORWARD_REFERENCES_CAPACITY_INCREMENT];
|
||||||
|
System.arraycopy(forwardReferences, 0, newValues, 0, forwardReferences.length);
|
||||||
|
forwardReferences = newValues;
|
||||||
|
}
|
||||||
|
forwardReferences[++lastElementIndex] = sourceInsnBytecodeOffset;
|
||||||
|
forwardReferences[++lastElementIndex] = referenceType | referenceHandle;
|
||||||
|
forwardReferences[0] = lastElementIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bytecode offset of this label to the given value and resolves the forward references
|
||||||
|
* to this label, if any. This method must be called when this label is added to the bytecode of
|
||||||
|
* the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that
|
||||||
|
* where left in the bytecode by each forward reference previously added to this label.
|
||||||
|
*
|
||||||
|
* @param code the bytecode of the method.
|
||||||
|
* @param bytecodeOffset the bytecode offset of this label.
|
||||||
|
* @return {@literal true} if a blank that was left for this label was too small to store the
|
||||||
|
* offset. In such a case the corresponding jump instruction is replaced with an equivalent
|
||||||
|
* ASM specific instruction using an unsigned two bytes offset. These ASM specific
|
||||||
|
* instructions are later replaced with standard bytecode instructions with wider offsets (4
|
||||||
|
* bytes instead of 2), in ClassReader.
|
||||||
|
*/
|
||||||
|
final boolean resolve(final byte[] code, final int bytecodeOffset) {
|
||||||
|
this.flags |= FLAG_RESOLVED;
|
||||||
|
this.bytecodeOffset = bytecodeOffset;
|
||||||
|
if (forwardReferences == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean hasAsmInstructions = false;
|
||||||
|
for (int i = forwardReferences[0]; i > 0; i -= 2) {
|
||||||
|
final int sourceInsnBytecodeOffset = forwardReferences[i - 1];
|
||||||
|
final int reference = forwardReferences[i];
|
||||||
|
final int relativeOffset = bytecodeOffset - sourceInsnBytecodeOffset;
|
||||||
|
int handle = reference & FORWARD_REFERENCE_HANDLE_MASK;
|
||||||
|
if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) {
|
||||||
|
if (relativeOffset < Short.MIN_VALUE || relativeOffset > Short.MAX_VALUE) {
|
||||||
|
// Change the opcode of the jump instruction, in order to be able to find it later in
|
||||||
|
// ClassReader. These ASM specific opcodes are similar to jump instruction opcodes, except
|
||||||
|
// that the 2 bytes offset is unsigned (and can therefore represent values from 0 to
|
||||||
|
// 65535, which is sufficient since the size of a method is limited to 65535 bytes).
|
||||||
|
int opcode = code[sourceInsnBytecodeOffset] & 0xFF;
|
||||||
|
if (opcode < Opcodes.IFNULL) {
|
||||||
|
// Change IFEQ ... JSR to ASM_IFEQ ... ASM_JSR.
|
||||||
|
code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_OPCODE_DELTA);
|
||||||
|
} else {
|
||||||
|
// Change IFNULL and IFNONNULL to ASM_IFNULL and ASM_IFNONNULL.
|
||||||
|
code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_IFNULL_OPCODE_DELTA);
|
||||||
|
}
|
||||||
|
hasAsmInstructions = true;
|
||||||
|
}
|
||||||
|
code[handle++] = (byte) (relativeOffset >>> 8);
|
||||||
|
code[handle] = (byte) relativeOffset;
|
||||||
|
} else {
|
||||||
|
code[handle++] = (byte) (relativeOffset >>> 24);
|
||||||
|
code[handle++] = (byte) (relativeOffset >>> 16);
|
||||||
|
code[handle++] = (byte) (relativeOffset >>> 8);
|
||||||
|
code[handle] = (byte) relativeOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasAsmInstructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
// Methods related to subroutines
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the basic blocks that belong to the subroutine starting with the basic block
|
||||||
|
* corresponding to this label, and marks these blocks as belonging to this subroutine. This
|
||||||
|
* method follows the control flow graph to find all the blocks that are reachable from the
|
||||||
|
* current basic block WITHOUT following any jsr target.
|
||||||
|
*
|
||||||
|
* <p>Note: a precondition and postcondition of this method is that all labels must have a null
|
||||||
|
* {@link #nextListElement}.
|
||||||
|
*
|
||||||
|
* @param subroutineId the id of the subroutine starting with the basic block corresponding to
|
||||||
|
* this label.
|
||||||
|
*/
|
||||||
|
final void markSubroutine(final short subroutineId) {
|
||||||
|
// Data flow algorithm: put this basic block in a list of blocks to process (which are blocks
|
||||||
|
// belonging to subroutine subroutineId) and, while there are blocks to process, remove one from
|
||||||
|
// the list, mark it as belonging to the subroutine, and add its successor basic blocks in the
|
||||||
|
// control flow graph to the list of blocks to process (if not already done).
|
||||||
|
Label listOfBlocksToProcess = this;
|
||||||
|
listOfBlocksToProcess.nextListElement = EMPTY_LIST;
|
||||||
|
while (listOfBlocksToProcess != EMPTY_LIST) {
|
||||||
|
// Remove a basic block from the list of blocks to process.
|
||||||
|
Label basicBlock = listOfBlocksToProcess;
|
||||||
|
listOfBlocksToProcess = listOfBlocksToProcess.nextListElement;
|
||||||
|
basicBlock.nextListElement = null;
|
||||||
|
|
||||||
|
// If it is not already marked as belonging to a subroutine, mark it as belonging to
|
||||||
|
// subroutineId and add its successors to the list of blocks to process (unless already done).
|
||||||
|
if (basicBlock.subroutineId == 0) {
|
||||||
|
basicBlock.subroutineId = subroutineId;
|
||||||
|
listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the basic blocks that end a subroutine starting with the basic block corresponding to
|
||||||
|
* this label and, for each one of them, adds an outgoing edge to the basic block following the
|
||||||
|
* given subroutine call. In other words, completes the control flow graph by adding the edges
|
||||||
|
* corresponding to the return from this subroutine, when called from the given caller basic
|
||||||
|
* block.
|
||||||
|
*
|
||||||
|
* <p>Note: a precondition and postcondition of this method is that all labels must have a null
|
||||||
|
* {@link #nextListElement}.
|
||||||
|
*
|
||||||
|
* @param subroutineCaller a basic block that ends with a jsr to the basic block corresponding to
|
||||||
|
* this label. This label is supposed to correspond to the start of a subroutine.
|
||||||
|
*/
|
||||||
|
final void addSubroutineRetSuccessors(final Label subroutineCaller) {
|
||||||
|
// Data flow algorithm: put this basic block in a list blocks to process (which are blocks
|
||||||
|
// belonging to a subroutine starting with this label) and, while there are blocks to process,
|
||||||
|
// remove one from the list, put it in a list of blocks that have been processed, add a return
|
||||||
|
// edge to the successor of subroutineCaller if applicable, and add its successor basic blocks
|
||||||
|
// in the control flow graph to the list of blocks to process (if not already done).
|
||||||
|
Label listOfProcessedBlocks = EMPTY_LIST;
|
||||||
|
Label listOfBlocksToProcess = this;
|
||||||
|
listOfBlocksToProcess.nextListElement = EMPTY_LIST;
|
||||||
|
while (listOfBlocksToProcess != EMPTY_LIST) {
|
||||||
|
// Move a basic block from the list of blocks to process to the list of processed blocks.
|
||||||
|
Label basicBlock = listOfBlocksToProcess;
|
||||||
|
listOfBlocksToProcess = basicBlock.nextListElement;
|
||||||
|
basicBlock.nextListElement = listOfProcessedBlocks;
|
||||||
|
listOfProcessedBlocks = basicBlock;
|
||||||
|
|
||||||
|
// Add an edge from this block to the successor of the caller basic block, if this block is
|
||||||
|
// the end of a subroutine and if this block and subroutineCaller do not belong to the same
|
||||||
|
// subroutine.
|
||||||
|
if ((basicBlock.flags & FLAG_SUBROUTINE_END) != 0
|
||||||
|
&& basicBlock.subroutineId != subroutineCaller.subroutineId) {
|
||||||
|
basicBlock.outgoingEdges =
|
||||||
|
new Edge(
|
||||||
|
basicBlock.outputStackSize,
|
||||||
|
// By construction, the first outgoing edge of a basic block that ends with a jsr
|
||||||
|
// instruction leads to the jsr continuation block, i.e. where execution continues
|
||||||
|
// when ret is called (see {@link #FLAG_SUBROUTINE_CALLER}).
|
||||||
|
subroutineCaller.outgoingEdges.successor,
|
||||||
|
basicBlock.outgoingEdges);
|
||||||
|
}
|
||||||
|
// Add its successors to the list of blocks to process. Note that {@link #pushSuccessors} does
|
||||||
|
// not push basic blocks which are already in a list. Here this means either in the list of
|
||||||
|
// blocks to process, or in the list of already processed blocks. This second list is
|
||||||
|
// important to make sure we don't reprocess an already processed block.
|
||||||
|
listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess);
|
||||||
|
}
|
||||||
|
// Reset the {@link #nextListElement} of all the basic blocks that have been processed to null,
|
||||||
|
// so that this method can be called again with a different subroutine or subroutine caller.
|
||||||
|
while (listOfProcessedBlocks != EMPTY_LIST) {
|
||||||
|
Label newListOfProcessedBlocks = listOfProcessedBlocks.nextListElement;
|
||||||
|
listOfProcessedBlocks.nextListElement = null;
|
||||||
|
listOfProcessedBlocks = newListOfProcessedBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the successors of this label in the method's control flow graph (except those
|
||||||
|
* corresponding to a jsr target, and those already in a list of labels) to the given list of
|
||||||
|
* blocks to process, and returns the new list.
|
||||||
|
*
|
||||||
|
* @param listOfLabelsToProcess a list of basic blocks to process, linked together with their
|
||||||
|
* {@link #nextListElement} field.
|
||||||
|
* @return the new list of blocks to process.
|
||||||
|
*/
|
||||||
|
private Label pushSuccessors(final Label listOfLabelsToProcess) {
|
||||||
|
Label newListOfLabelsToProcess = listOfLabelsToProcess;
|
||||||
|
Edge outgoingEdge = outgoingEdges;
|
||||||
|
while (outgoingEdge != null) {
|
||||||
|
// By construction, the second outgoing edge of a basic block that ends with a jsr instruction
|
||||||
|
// leads to the jsr target (see {@link #FLAG_SUBROUTINE_CALLER}).
|
||||||
|
boolean isJsrTarget =
|
||||||
|
(flags & Label.FLAG_SUBROUTINE_CALLER) != 0 && outgoingEdge == outgoingEdges.nextEdge;
|
||||||
|
if (!isJsrTarget && outgoingEdge.successor.nextListElement == null) {
|
||||||
|
// Add this successor to the list of blocks to process, if it does not already belong to a
|
||||||
|
// list of labels.
|
||||||
|
outgoingEdge.successor.nextListElement = newListOfLabelsToProcess;
|
||||||
|
newListOfLabelsToProcess = outgoingEdge.successor;
|
||||||
|
}
|
||||||
|
outgoingEdge = outgoingEdge.nextEdge;
|
||||||
|
}
|
||||||
|
return newListOfLabelsToProcess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
// Overridden Object methods
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of this label.
|
* Returns a string representation of this label.
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is available under and governed by the GNU General Public
|
||||||
|
* License version 2 only, as published by the Free Software Foundation.
|
||||||
|
* However, the following notice accompanied the original version of this
|
||||||
|
* file:
|
||||||
|
*
|
||||||
|
* ASM: a very small and fast Java bytecode manipulation framework
|
||||||
|
* Copyright (c) 2000-2011 INRIA, France Telecom
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of the copyright holders nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package jdk.internal.org.objectweb.asm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when the Code attribute of a method produced by a {@link ClassWriter} is too
|
||||||
|
* large.
|
||||||
|
*
|
||||||
|
* @author Jason Zaugg
|
||||||
|
*/
|
||||||
|
public final class MethodTooLargeException extends IndexOutOfBoundsException {
|
||||||
|
private static final long serialVersionUID = 6807380416709738314L;
|
||||||
|
|
||||||
|
private final String className;
|
||||||
|
private final String methodName;
|
||||||
|
private final String descriptor;
|
||||||
|
private final int codeSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@link MethodTooLargeException}.
|
||||||
|
*
|
||||||
|
* @param className the internal name of the owner class.
|
||||||
|
* @param methodName the name of the method.
|
||||||
|
* @param descriptor the descriptor of the method.
|
||||||
|
* @param codeSize the size of the method's Code attribute, in bytes.
|
||||||
|
*/
|
||||||
|
public MethodTooLargeException(
|
||||||
|
final String className,
|
||||||
|
final String methodName,
|
||||||
|
final String descriptor,
|
||||||
|
final int codeSize) {
|
||||||
|
super("Method too large: " + className + "." + methodName + " " + descriptor);
|
||||||
|
this.className = className;
|
||||||
|
this.methodName = methodName;
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
this.codeSize = codeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the internal name of the owner class.
|
||||||
|
*
|
||||||
|
* @return the internal name of the owner class.
|
||||||
|
*/
|
||||||
|
public String getClassName() {
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the method.
|
||||||
|
*
|
||||||
|
* @return the name of the method.
|
||||||
|
*/
|
||||||
|
public String getMethodName() {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the descriptor of the method.
|
||||||
|
*
|
||||||
|
* @return the descriptor of the method.
|
||||||
|
*/
|
||||||
|
public String getDescriptor() {
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the method's Code attribute, in bytes.
|
||||||
|
*
|
||||||
|
* @return the size of the method's Code attribute, in bytes.
|
||||||
|
*/
|
||||||
|
public int getCodeSize() {
|
||||||
|
return codeSize;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user