diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad
index b82ee59b99c..3e1a6cd0a54 100644
--- a/src/hotspot/cpu/aarch64/aarch64.ad
+++ b/src/hotspot/cpu/aarch64/aarch64.ad
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
// Copyright (c) 2014, 2021, Red Hat, Inc. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
@@ -2732,11 +2732,8 @@ bool is_vector_arith_imm_pattern(Node* n, Node* m) {
// Should the matcher clone input 'm' of node 'n'?
bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) {
// ShiftV src (ShiftCntV con)
- // StoreVector (VectorStoreMask src)
// Binary src (Replicate con)
- if (is_vshift_con_pattern(n, m) ||
- (UseSVE > 0 && m->Opcode() == Op_VectorStoreMask && n->Opcode() == Op_StoreVector) ||
- is_vector_arith_imm_pattern(n, m)) {
+ if (is_vshift_con_pattern(n, m) || is_vector_arith_imm_pattern(n, m)) {
mstack.push(m, Visit);
return true;
}
diff --git a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
index 0a97872900e..9f60e493d6a 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRGenerator_aarch64.cpp
@@ -148,7 +148,7 @@ LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index,
if (index->is_constant()) {
LIR_Const *constant = index->as_constant_ptr();
if (constant->type() == T_INT) {
- large_disp += index->as_jint() << shift;
+ large_disp += ((intx)index->as_jint()) << shift;
} else {
assert(constant->type() == T_LONG, "should be");
jlong c = index->as_jlong() << shift;
@@ -194,7 +194,7 @@ LIR_Address* LIRGenerator::generate_address(LIR_Opr base, LIR_Opr index,
if (large_disp == 0 && index->is_register()) {
return new LIR_Address(base, index, type);
} else {
- assert(Address::offset_ok_for_immed(large_disp, 0), "must be");
+ assert(Address::offset_ok_for_immed(large_disp, shift), "failed for large_disp: " INTPTR_FORMAT " and shift %d", large_disp, shift);
return new LIR_Address(base, large_disp, type);
}
}
@@ -204,24 +204,7 @@ LIR_Address* LIRGenerator::emit_array_address(LIR_Opr array_opr, LIR_Opr index_o
int offset_in_bytes = arrayOopDesc::base_offset_in_bytes(type);
int elem_size = type2aelembytes(type);
int shift = exact_log2(elem_size);
-
- LIR_Address* addr;
- if (index_opr->is_constant()) {
- addr = new LIR_Address(array_opr,
- offset_in_bytes + (intx)(index_opr->as_jint()) * elem_size, type);
- } else {
- if (offset_in_bytes) {
- LIR_Opr tmp = new_pointer_register();
- __ add(array_opr, LIR_OprFact::intConst(offset_in_bytes), tmp);
- array_opr = tmp;
- offset_in_bytes = 0;
- }
- addr = new LIR_Address(array_opr,
- index_opr,
- LIR_Address::scale(type),
- offset_in_bytes, type);
- }
- return addr;
+ return generate_address(array_opr, index_opr, shift, offset_in_bytes, type);
}
LIR_Opr LIRGenerator::load_immediate(int x, BasicType type) {
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index cce3d497bbc..69124c299c1 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -29,6 +29,7 @@
#include "jvm.h"
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
+#include "ci/ciEnv.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
@@ -159,8 +160,7 @@ int MacroAssembler::pd_patch_instruction_size(address branch, address target) {
Instruction_aarch64::patch(branch+8, 20, 5, (dest >>= 16) & 0xffff);
assert(target_addr_for_insn(branch) == target, "should be");
instructions = 3;
- } else if (Instruction_aarch64::extract(insn, 31, 22) == 0b1011100101 &&
- Instruction_aarch64::extract(insn, 4, 0) == 0b11111) {
+ } else if (NativeInstruction::is_ldrw_to_zr(address(&insn))) {
// nothing to do
assert(target == 0, "did not expect to relocate target for polling page load");
} else {
@@ -283,15 +283,19 @@ address MacroAssembler::target_addr_for_insn(address insn_addr, unsigned insn) {
return address(uint64_t(Instruction_aarch64::extract(insns[0], 20, 5))
+ (uint64_t(Instruction_aarch64::extract(insns[1], 20, 5)) << 16)
+ (uint64_t(Instruction_aarch64::extract(insns[2], 20, 5)) << 32));
- } else if (Instruction_aarch64::extract(insn, 31, 22) == 0b1011100101 &&
- Instruction_aarch64::extract(insn, 4, 0) == 0b11111) {
- return 0;
} else {
ShouldNotReachHere();
}
return address(((uint64_t)insn_addr + (offset << 2)));
}
+address MacroAssembler::target_addr_for_insn_or_null(address insn_addr, unsigned insn) {
+ if (NativeInstruction::is_ldrw_to_zr(address(&insn))) {
+ return 0;
+ }
+ return MacroAssembler::target_addr_for_insn(insn_addr, insn);
+}
+
void MacroAssembler::safepoint_poll(Label& slow_path, bool at_return, bool acquire, bool in_nmethod) {
if (acquire) {
lea(rscratch1, Address(rthread, JavaThread::polling_word_offset()));
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
index dcef7b34d73..16f9790bde4 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -600,10 +600,15 @@ public:
static bool uses_implicit_null_check(void* address);
static address target_addr_for_insn(address insn_addr, unsigned insn);
+ static address target_addr_for_insn_or_null(address insn_addr, unsigned insn);
static address target_addr_for_insn(address insn_addr) {
unsigned insn = *(unsigned*)insn_addr;
return target_addr_for_insn(insn_addr, insn);
}
+ static address target_addr_for_insn_or_null(address insn_addr) {
+ unsigned insn = *(unsigned*)insn_addr;
+ return target_addr_for_insn_or_null(insn_addr, insn);
+ }
// Required platform-specific helpers for Label::patch_instructions.
// They _shadow_ the declarations in AbstractAssembler, which are undefined.
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
index d808e4b5b53..7599c21c2fc 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -315,7 +315,7 @@ void NativeMovRegMem::set_offset(int x) {
void NativeMovRegMem::verify() {
#ifdef ASSERT
- address dest = MacroAssembler::target_addr_for_insn(instruction_address());
+ address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address());
#endif
}
@@ -329,7 +329,7 @@ void NativeJump::check_verified_entry_alignment(address entry, address verified_
address NativeJump::jump_destination() const {
- address dest = MacroAssembler::target_addr_for_insn(instruction_address());
+ address dest = MacroAssembler::target_addr_for_insn_or_null(instruction_address());
// We use jump to self as the unresolved address which the inline
// cache code (and relocs) know about
diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp
index 31a2b287442..b8e6433913b 100644
--- a/src/hotspot/cpu/ppc/frame_ppc.cpp
+++ b/src/hotspot/cpu/ppc/frame_ppc.cpp
@@ -302,7 +302,8 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) {
return false;
}
- if (fp() - (abi_minframe_size + ijava_state_size) < sp()) {
+ int min_frame_slots = (abi_minframe_size + ijava_state_size) / sizeof(intptr_t);
+ if (fp() - min_frame_slots < sp()) {
return false;
}
// These are hacks to keep us out of trouble.
diff --git a/src/hotspot/cpu/s390/frame_s390.cpp b/src/hotspot/cpu/s390/frame_s390.cpp
index 72e1dffe57d..bfb38ffcdaf 100644
--- a/src/hotspot/cpu/s390/frame_s390.cpp
+++ b/src/hotspot/cpu/s390/frame_s390.cpp
@@ -306,7 +306,8 @@ bool frame::is_interpreted_frame_valid(JavaThread* thread) const {
if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) {
return false;
}
- if (fp() - (z_abi_16_size + z_ijava_state_size) < sp()) {
+ int min_frame_slots = (z_abi_16_size + z_ijava_state_size) / sizeof(intptr_t);
+ if (fp() - min_frame_slots < sp()) {
return false;
}
// These are hacks to keep us out of trouble.
diff --git a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
index 0b62108c79f..819c36957b3 100644
--- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
+++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp
@@ -192,8 +192,32 @@ LIR_Address* LIRGenerator::emit_array_address(LIR_Opr array_opr, LIR_Opr index_o
LIR_Address* addr;
if (index_opr->is_constant()) {
int elem_size = type2aelembytes(type);
- addr = new LIR_Address(array_opr,
- offset_in_bytes + (intx)(index_opr->as_jint()) * elem_size, type);
+#ifdef _LP64
+ jint index = index_opr->as_jint();
+ jlong disp = offset_in_bytes + (jlong)(index) * elem_size;
+ if (disp > max_jint) {
+ // Displacement overflow. Cannot directly use instruction with 32-bit displacement for 64-bit addresses.
+ // Convert array index to long to do array offset computation with 64-bit values.
+ index_opr = new_register(T_LONG);
+ __ move(LIR_OprFact::longConst(index), index_opr);
+ addr = new LIR_Address(array_opr, index_opr, LIR_Address::scale(type), offset_in_bytes, type);
+ } else {
+ addr = new LIR_Address(array_opr, (intx)disp, type);
+ }
+#else
+ // A displacement overflow can also occur for x86 but that is not a problem due to the 32-bit address range!
+ // Let's assume an array 'a' and an access with displacement 'disp'. When disp overflows, then "a + disp" will
+ // always be negative (i.e. underflows the 32-bit address range):
+ // Let N = 2^32: a + signed_overflow(disp) = a + disp - N.
+ // "a + disp" is always smaller than N. If an index was chosen which would point to an address beyond N, then
+ // range checks would catch that and throw an exception. Thus, a + disp < 0 holds which means that it always
+ // underflows the 32-bit address range:
+ // unsigned_underflow(a + signed_overflow(disp)) = unsigned_underflow(a + disp - N)
+ // = (a + disp - N) + N = a + disp
+ // This shows that we still end up at the correct address with a displacement overflow due to the 32-bit address
+ // range limitation. This overflow only needs to be handled if addresses can be larger as on 64-bit platforms.
+ addr = new LIR_Address(array_opr, offset_in_bytes + (intx)(index_opr->as_jint()) * elem_size, type);
+#endif // _LP64
} else {
#ifdef _LP64
if (index_opr->type() == T_INT) {
diff --git a/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp
index d09608c6aa7..15f6220fc81 100644
--- a/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp
+++ b/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
+ * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,14 +58,15 @@ bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext,
// if we were running Java code when SIGPROF came in.
if (isInJava) {
ucontext_t* uc = (ucontext_t*) ucontext;
- frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/],
- (address)uc->uc_mcontext.regs->nip);
+ address pc = (address)uc->uc_mcontext.regs->nip;
- if (ret_frame.pc() == NULL) {
+ if (pc == NULL) {
// ucontext wasn't useful
return false;
}
+ frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], pc);
+
if (ret_frame.fp() == NULL) {
// The found frame does not have a valid frame pointer.
// Bail out because this will create big trouble later on, either
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index 3adfa65c1e7..e06251700bd 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -2036,19 +2036,19 @@ void GraphBuilder::invoke(Bytecodes::Code code) {
cha_monomorphic_target = target->find_monomorphic_target(calling_klass, declared_interface, singleton);
if (cha_monomorphic_target != NULL) {
if (cha_monomorphic_target->holder() != compilation()->env()->Object_klass()) {
- // If CHA is able to bind this invoke then update the class
- // to match that class, otherwise klass will refer to the
- // interface.
- klass = cha_monomorphic_target->holder();
+ ciInstanceKlass* holder = cha_monomorphic_target->holder();
+ ciInstanceKlass* constraint = (holder->is_subtype_of(singleton) ? holder : singleton); // avoid upcasts
actual_recv = declared_interface;
// insert a check it's really the expected class.
- CheckCast* c = new CheckCast(klass, receiver, copy_state_for_exception());
+ CheckCast* c = new CheckCast(constraint, receiver, copy_state_for_exception());
c->set_incompatible_class_change_check();
- c->set_direct_compare(klass->is_final());
+ c->set_direct_compare(constraint->is_final());
// pass the result of the checkcast so that the compiler has
// more accurate type info in the inlinee
better_receiver = append_split(c);
+
+ dependency_recorder()->assert_unique_implementor(declared_interface, singleton);
} else {
cha_monomorphic_target = NULL; // subtype check against Object is useless
}
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index e9c813ce4fb..2325241dc64 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -3053,6 +3053,7 @@ static int inner_classes_jump_to_outer(const Array* inner_classes, int inner
static bool inner_classes_check_loop_through_outer(const Array* inner_classes, int idx, const ConstantPool* cp, int length) {
int slow = inner_classes->at(idx + InstanceKlass::inner_class_inner_class_info_offset);
int fast = inner_classes->at(idx + InstanceKlass::inner_class_outer_class_info_offset);
+
while (fast != -1 && fast != 0) {
if (slow != 0 && (cp->klass_name_at(slow) == cp->klass_name_at(fast))) {
return true; // found a circularity
@@ -3082,14 +3083,15 @@ bool ClassFileParser::check_inner_classes_circularity(const ConstantPool* cp, in
for (int y = idx + InstanceKlass::inner_class_next_offset; y < length;
y += InstanceKlass::inner_class_next_offset) {
- // To maintain compatibility, throw an exception if duplicate inner classes
- // entries are found.
- guarantee_property((_inner_classes->at(idx) != _inner_classes->at(y) ||
- _inner_classes->at(idx+1) != _inner_classes->at(y+1) ||
- _inner_classes->at(idx+2) != _inner_classes->at(y+2) ||
- _inner_classes->at(idx+3) != _inner_classes->at(y+3)),
- "Duplicate entry in InnerClasses attribute in class file %s",
- CHECK_(true));
+ // 4347400: make sure there's no duplicate entry in the classes array
+ if (_major_version >= JAVA_1_5_VERSION) {
+ guarantee_property((_inner_classes->at(idx) != _inner_classes->at(y) ||
+ _inner_classes->at(idx+1) != _inner_classes->at(y+1) ||
+ _inner_classes->at(idx+2) != _inner_classes->at(y+2) ||
+ _inner_classes->at(idx+3) != _inner_classes->at(y+3)),
+ "Duplicate entry in InnerClasses attribute in class file %s",
+ CHECK_(true));
+ }
// Return true if there are two entries with the same inner_class_info_index.
if (_inner_classes->at(y) == _inner_classes->at(idx)) {
return true;
@@ -3182,10 +3184,9 @@ u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStrea
inner_classes->at_put(index++, inner_access_flags.as_short());
}
- // 4347400: make sure there's no duplicate entry in the classes array
- // Also, check for circular entries.
+ // Check for circular and duplicate entries.
bool has_circularity = false;
- if (_need_verify && _major_version >= JAVA_1_5_VERSION) {
+ if (_need_verify) {
has_circularity = check_inner_classes_circularity(cp, length * 4, CHECK_0);
if (has_circularity) {
// If circularity check failed then ignore InnerClasses attribute.
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index d5ac1c308fd..f42b1fe4751 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -303,13 +303,19 @@ u1* ClassPathZipEntry::open_entry(JavaThread* current, const char* name, jint* f
}
// read contents into resource array
- int size = (*filesize) + ((nul_terminate) ? 1 : 0);
+ size_t size = (uint32_t)(*filesize);
+ if (nul_terminate) {
+ if (sizeof(size) == sizeof(uint32_t) && size == UINT_MAX) {
+ return NULL; // 32-bit integer overflow will occur.
+ }
+ size++;
+ }
buffer = NEW_RESOURCE_ARRAY(u1, size);
if (!(*ReadEntry)(_zip, entry, buffer, filename)) return NULL;
// return result
if (nul_terminate) {
- buffer[*filesize] = 0;
+ buffer[size - 1] = 0;
}
return buffer;
}
diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp
index 8ca9c59fc82..e150b300b7b 100644
--- a/src/hotspot/share/code/dependencies.cpp
+++ b/src/hotspot/share/code/dependencies.cpp
@@ -116,6 +116,12 @@ void Dependencies::assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm,
}
}
+void Dependencies::assert_unique_implementor(ciInstanceKlass* ctxk, ciInstanceKlass* uniqk) {
+ check_ctxk(ctxk);
+ check_unique_implementor(ctxk, uniqk);
+ assert_common_2(unique_implementor, ctxk, uniqk);
+}
+
void Dependencies::assert_has_no_finalizable_subclasses(ciKlass* ctxk) {
check_ctxk(ctxk);
assert_common_1(no_finalizable_subclasses, ctxk);
@@ -173,6 +179,13 @@ void Dependencies::assert_abstract_with_unique_concrete_subtype(Klass* ctxk, Kla
assert_common_2(abstract_with_unique_concrete_subtype, ctxk_dv, conck_dv);
}
+void Dependencies::assert_unique_implementor(InstanceKlass* ctxk, InstanceKlass* uniqk) {
+ check_ctxk(ctxk);
+ assert(ctxk->is_interface(), "not an interface");
+ assert(ctxk->implementor() == uniqk, "not a unique implementor");
+ assert_common_2(unique_implementor, DepValue(_oop_recorder, ctxk), DepValue(_oop_recorder, uniqk));
+}
+
void Dependencies::assert_unique_concrete_method(Klass* ctxk, Method* uniqm) {
check_ctxk(ctxk);
check_unique_method(ctxk, uniqm);
@@ -580,6 +593,7 @@ const char* Dependencies::_dep_name[TYPE_LIMIT] = {
"abstract_with_unique_concrete_subtype",
"unique_concrete_method_2",
"unique_concrete_method_4",
+ "unique_implementor",
"no_finalizable_subclasses",
"call_site_target_value"
};
@@ -591,6 +605,7 @@ int Dependencies::_dep_args[TYPE_LIMIT] = {
2, // abstract_with_unique_concrete_subtype ctxk, k
2, // unique_concrete_method_2 ctxk, m
4, // unique_concrete_method_4 ctxk, m, resolved_klass, resolved_method
+ 2, // unique_implementor ctxk, implementor
1, // no_finalizable_subclasses ctxk
2 // call_site_target_value call_site, method_handle
};
@@ -1813,6 +1828,16 @@ Klass* Dependencies::check_unique_concrete_method(InstanceKlass* ctxk,
return NULL;
}
+Klass* Dependencies::check_unique_implementor(InstanceKlass* ctxk, Klass* uniqk, NewKlassDepChange* changes) {
+ assert(ctxk->is_interface(), "sanity");
+ assert(ctxk->nof_implementors() > 0, "no implementors");
+ if (ctxk->nof_implementors() == 1) {
+ assert(ctxk->implementor() == uniqk, "sanity");
+ return NULL;
+ }
+ return ctxk; // no unique implementor
+}
+
// Search for AME.
// There are two version of checks.
// 1) Spot checking version(Classload time). Newly added class is checked for AME.
@@ -2062,6 +2087,9 @@ Klass* Dependencies::DepStream::check_new_klass_dependency(NewKlassDepChange* ch
case unique_concrete_method_4:
witness = check_unique_concrete_method(context_type(), method_argument(1), type_argument(2), method_argument(3), changes);
break;
+ case unique_implementor:
+ witness = check_unique_implementor(context_type(), type_argument(1), changes);
+ break;
case no_finalizable_subclasses:
witness = check_has_no_finalizable_subclasses(context_type(), changes);
break;
diff --git a/src/hotspot/share/code/dependencies.hpp b/src/hotspot/share/code/dependencies.hpp
index 104fc9ee649..0d8fa9fa48c 100644
--- a/src/hotspot/share/code/dependencies.hpp
+++ b/src/hotspot/share/code/dependencies.hpp
@@ -143,6 +143,9 @@ class Dependencies: public ResourceObj {
// of the analysis.
unique_concrete_method_4, // one unique concrete method under CX
+ // This dependency asserts that interface CX has a unique implementor class.
+ unique_implementor, // one unique implementor under CX
+
// This dependency asserts that no instances of class or it's
// subclasses require finalization registration.
no_finalizable_subclasses,
@@ -329,7 +332,10 @@ class Dependencies: public ResourceObj {
assert(!is_concrete_klass(ctxk->as_instance_klass()), "must be abstract");
}
static void check_unique_method(ciKlass* ctxk, ciMethod* m) {
- assert(!m->can_be_statically_bound(ctxk->as_instance_klass()), "redundant");
+ assert(!m->can_be_statically_bound(ctxk->as_instance_klass()) || ctxk->is_interface(), "redundant");
+ }
+ static void check_unique_implementor(ciInstanceKlass* ctxk, ciInstanceKlass* uniqk) {
+ assert(ctxk->implementor() == uniqk, "not a unique implementor");
}
void assert_common_1(DepType dept, ciBaseObject* x);
@@ -343,9 +349,9 @@ class Dependencies: public ResourceObj {
void assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, ciKlass* conck);
void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm);
void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm, ciKlass* resolved_klass, ciMethod* resolved_method);
+ void assert_unique_implementor(ciInstanceKlass* ctxk, ciInstanceKlass* uniqk);
void assert_has_no_finalizable_subclasses(ciKlass* ctxk);
void assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle);
-
#if INCLUDE_JVMCI
private:
static void check_ctxk(Klass* ctxk) {
@@ -366,6 +372,7 @@ class Dependencies: public ResourceObj {
void assert_evol_method(Method* m);
void assert_has_no_finalizable_subclasses(Klass* ctxk);
void assert_leaf_type(Klass* ctxk);
+ void assert_unique_implementor(InstanceKlass* ctxk, InstanceKlass* uniqk);
void assert_unique_concrete_method(Klass* ctxk, Method* uniqm);
void assert_abstract_with_unique_concrete_subtype(Klass* ctxk, Klass* conck);
void assert_call_site_target_value(oop callSite, oop methodHandle);
@@ -413,6 +420,7 @@ class Dependencies: public ResourceObj {
static Klass* check_evol_method(Method* m);
static Klass* check_leaf_type(InstanceKlass* ctxk);
static Klass* check_abstract_with_unique_concrete_subtype(InstanceKlass* ctxk, Klass* conck, NewKlassDepChange* changes = NULL);
+ static Klass* check_unique_implementor(InstanceKlass* ctxk, Klass* uniqk, NewKlassDepChange* changes = NULL);
static Klass* check_unique_concrete_method(InstanceKlass* ctxk, Method* uniqm, NewKlassDepChange* changes = NULL);
static Klass* check_unique_concrete_method(InstanceKlass* ctxk, Method* uniqm, Klass* resolved_klass, Method* resolved_method, KlassDepChange* changes = NULL);
static Klass* check_has_no_finalizable_subclasses(InstanceKlass* ctxk, NewKlassDepChange* changes = NULL);
diff --git a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp
index 9819328ee66..8f704fce350 100644
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -123,7 +123,13 @@ void G1DirtyCardQueueSet::enqueue_completed_buffer(BufferNode* cbn) {
// Increment _num_cards before adding to queue, so queue removal doesn't
// need to deal with _num_cards possibly going negative.
size_t new_num_cards = Atomic::add(&_num_cards, buffer_size() - cbn->index());
- _completed.push(*cbn);
+ {
+ // Perform push in CS. The old tail may be popped while the push is
+ // observing it (attaching it to the new buffer). We need to ensure it
+ // can't be reused until the push completes, to avoid ABA problems.
+ GlobalCounter::CriticalSection cs(Thread::current());
+ _completed.push(*cbn);
+ }
if ((new_num_cards > process_cards_threshold()) &&
(_primary_refinement_thread != NULL)) {
_primary_refinement_thread->activate();
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp
index 29bade3def6..9b7ddcd1273 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceIdLoadBarrier.inline.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -109,7 +109,14 @@ inline traceid JfrTraceIdLoadBarrier::load(const PackageEntry* package) {
inline traceid JfrTraceIdLoadBarrier::load(const ClassLoaderData* cld) {
assert(cld != NULL, "invariant");
- return cld->has_class_mirror_holder() ? 0 : set_used_and_get(cld);
+ if (cld->has_class_mirror_holder()) {
+ return 0;
+ }
+ const Klass* const class_loader_klass = cld->class_loader_klass();
+ if (class_loader_klass != nullptr && should_tag(class_loader_klass)) {
+ load_barrier(class_loader_klass);
+ }
+ return set_used_and_get(cld);
}
inline traceid JfrTraceIdLoadBarrier::load_leakp(const Klass* klass, const Method* method) {
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index fafc58fa9fd..e635b82f2ce 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -2417,7 +2417,9 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
} else {
it->push(&_default_vtable_indices);
}
- it->push(&_fields);
+
+ // _fields might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
+ it->push(&_fields, MetaspaceClosure::_writable);
if (itable_length() > 0) {
itableOffsetEntry* ioe = (itableOffsetEntry*)start_of_itable();
diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp
index f666af6b9ab..f034aacae62 100644
--- a/src/hotspot/share/opto/doCall.cpp
+++ b/src/hotspot/share/opto/doCall.cpp
@@ -332,8 +332,10 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
CallGenerator* miss_cg = CallGenerator::for_uncommon_trap(callee,
Deoptimization::Reason_class_check, Deoptimization::Action_none);
- CallGenerator* cg = CallGenerator::for_guarded_call(holder, miss_cg, hit_cg);
+ ciKlass* constraint = (holder->is_subclass_of(singleton) ? holder : singleton); // avoid upcasts
+ CallGenerator* cg = CallGenerator::for_guarded_call(constraint, miss_cg, hit_cg);
if (hit_cg != NULL && cg != NULL) {
+ dependencies()->assert_unique_implementor(declared_interface, singleton);
dependencies()->assert_unique_concrete_method(declared_interface, cha_monomorphic_target, declared_interface, callee);
return cg;
}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java
index b13a1a905d7..570aca63e09 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java
@@ -177,6 +177,10 @@ final class KeyProtector {
byte[] encodedParams =
encrInfo.getAlgorithm().getEncodedParams();
+ if (encodedParams == null) {
+ throw new IOException("Missing PBE parameters");
+ }
+
// parse the PBE parameters into the corresponding spec
AlgorithmParameters pbeParams =
AlgorithmParameters.getInstance("PBE");
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java
index fb8ff87cf2d..d8bf4386455 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/OAEPParameters.java
@@ -107,8 +107,12 @@ public final class OAEPParameters extends AlgorithmParametersSpi {
if (!val.getOID().equals(OID_MGF1)) {
throw new IOException("Only MGF1 mgf is supported");
}
+ byte[] encodedParams = val.getEncodedParams();
+ if (encodedParams == null) {
+ throw new IOException("Missing MGF1 parameters");
+ }
AlgorithmId params = AlgorithmId.parse(
- new DerValue(val.getEncodedParams()));
+ new DerValue(encodedParams));
mgfSpec = switch (params.getName()) {
case "SHA-1" -> MGF1ParameterSpec.SHA1;
case "SHA-224" -> MGF1ParameterSpec.SHA224;
@@ -129,7 +133,12 @@ public final class OAEPParameters extends AlgorithmParametersSpi {
if (!val.getOID().equals(OID_PSpecified)) {
throw new IOException("Wrong OID for pSpecified");
}
- p = DerValue.wrap(val.getEncodedParams()).getOctetString();
+ byte[] encodedParams = val.getEncodedParams();
+ if (encodedParams == null) {
+ throw new IOException("Missing pSpecified label");
+ }
+
+ p = DerValue.wrap(encodedParams).getOctetString();
} else {
p = new byte[0];
}
diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java
index 704960f1107..dec50deb9c9 100644
--- a/src/java.base/share/classes/java/io/ObjectInputStream.java
+++ b/src/java.base/share/classes/java/io/ObjectInputStream.java
@@ -1315,6 +1315,8 @@ public class ObjectInputStream
*
each object reference previously deserialized from the stream
* (class is {@code null}, arrayLength is -1),
*
each regular class (class is not {@code null}, arrayLength is -1),
+ *
each interface class explicitly referenced in the stream
+ * (it is not called for interfaces implemented by classes in the stream),
*
each interface of a dynamic proxy and the dynamic proxy class itself
* (class is not {@code null}, arrayLength is -1),
*
each array is filtered using the array type and length of the array
@@ -2071,6 +2073,30 @@ public class ObjectInputStream
totalObjectRefs++;
depth++;
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
+
+ if (cl != null) {
+ // Check that serial filtering has been done on the local class descriptor's superclass,
+ // in case it does not appear in the stream.
+
+ // Find the next super descriptor that has a local class descriptor.
+ // Descriptors for which there is no local class are ignored.
+ ObjectStreamClass superLocal = null;
+ for (ObjectStreamClass sDesc = desc.getSuperDesc(); sDesc != null; sDesc = sDesc.getSuperDesc()) {
+ if ((superLocal = sDesc.getLocalDesc()) != null) {
+ break;
+ }
+ }
+
+ // Scan local descriptor superclasses for a match with the local descriptor of the super found above.
+ // For each super descriptor before the match, invoke the serial filter on the class.
+ // The filter is invoked for each class that has not already been filtered
+ // but would be filtered if the instance had been serialized by this Java runtime.
+ for (ObjectStreamClass lDesc = desc.getLocalDesc().getSuperDesc();
+ lDesc != null && lDesc != superLocal;
+ lDesc = lDesc.getSuperDesc()) {
+ filterCheck(lDesc.forClass(), -1);
+ }
+ }
} finally {
depth--;
}
@@ -2529,6 +2555,13 @@ public class ObjectInputStream
throw new InternalError();
}
clear();
+ // Check that an object follows the TC_EXCEPTION typecode
+ byte tc = bin.peekByte();
+ if (tc != TC_OBJECT &&
+ tc != TC_REFERENCE) {
+ throw new StreamCorruptedException(
+ String.format("invalid type code: %02X", tc));
+ }
return (IOException) readObject0(Object.class, false);
}
diff --git a/src/java.base/share/classes/java/lang/StringBuffer.java b/src/java.base/share/classes/java/lang/StringBuffer.java
index e2ca48fdaf6..1ba12bd29bc 100644
--- a/src/java.base/share/classes/java/lang/StringBuffer.java
+++ b/src/java.base/share/classes/java/lang/StringBuffer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,12 @@
package java.lang;
import java.io.IOException;
-import java.util.Arrays;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.io.Serial;
+import java.io.Serializable;
+import java.io.StreamCorruptedException;
import jdk.internal.vm.annotation.IntrinsicCandidate;
/**
@@ -106,7 +111,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
*/
public final class StringBuffer
extends AbstractStringBuilder
- implements java.io.Serializable, Comparable, CharSequence
+ implements Serializable, Comparable, CharSequence
{
/**
@@ -116,7 +121,7 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
private transient String toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
- @java.io.Serial
+ @Serial
static final long serialVersionUID = 3388685877147921107L;
/**
@@ -725,25 +730,25 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* A flag indicating whether the backing array is shared.
* The value is ignored upon deserialization.
*/
- @java.io.Serial
- private static final java.io.ObjectStreamField[] serialPersistentFields =
+ @Serial
+ private static final ObjectStreamField[] serialPersistentFields =
{
- new java.io.ObjectStreamField("value", char[].class),
- new java.io.ObjectStreamField("count", Integer.TYPE),
- new java.io.ObjectStreamField("shared", Boolean.TYPE),
+ new ObjectStreamField("value", char[].class),
+ new ObjectStreamField("count", Integer.TYPE),
+ new ObjectStreamField("shared", Boolean.TYPE),
};
/**
- * The {@code writeObject} method is called to write the state of the {@code StringBuffer} to
- * a stream.
+ * The {@code writeObject} method is called to write the state of the
+ * {@code StringBuffer} to a stream.
*
* @param s the {@code ObjectOutputStream} to which data is written
* @throws IOException if an I/O error occurs
*/
- @java.io.Serial
- private synchronized void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
- java.io.ObjectOutputStream.PutField fields = s.putFields();
+ @Serial
+ private synchronized void writeObject(ObjectOutputStream s)
+ throws IOException {
+ ObjectOutputStream.PutField fields = s.putFields();
char[] val = new char[capacity()];
if (isLatin1()) {
StringLatin1.getChars(value, 0, count, val, 0);
@@ -757,20 +762,26 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
}
/**
- * The {@code readObject} method is called to restore the state of the {@code StringBuffer} from
- * a stream.
+ * The {@code readObject} method is called to restore the state of the
+ * {@code StringBuffer} from a stream.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
- @java.io.Serial
- private void readObject(java.io.ObjectInputStream s)
- throws java.io.IOException, ClassNotFoundException {
- java.io.ObjectInputStream.GetField fields = s.readFields();
+ @Serial
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ ObjectInputStream.GetField fields = s.readFields();
+
char[] val = (char[])fields.get("value", null);
+ int c = fields.get("count", 0);
+ if (c < 0 || c > val.length) {
+ throw new StreamCorruptedException("count value invalid");
+ }
initBytes(val, 0, val.length);
- count = fields.get("count", 0);
+ count = c;
+ // ignore shared field
}
synchronized void getBytes(byte[] dst, int dstBegin, byte coder) {
diff --git a/src/java.base/share/classes/java/lang/StringBuilder.java b/src/java.base/share/classes/java/lang/StringBuilder.java
index b22ed99dbfc..8e759c213a9 100644
--- a/src/java.base/share/classes/java/lang/StringBuilder.java
+++ b/src/java.base/share/classes/java/lang/StringBuilder.java
@@ -28,6 +28,10 @@ package java.lang;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serial;
+import java.io.StreamCorruptedException;
/**
* A mutable sequence of characters. This class provides an API compatible
@@ -90,7 +94,7 @@ public final class StringBuilder
{
/** use serialVersionUID for interoperability */
- @java.io.Serial
+ @Serial
static final long serialVersionUID = 4383685877147921099L;
/**
@@ -464,9 +468,8 @@ public final class StringBuilder
* @param s the {@code ObjectOutputStream} to which data is written
* @throws IOException if an I/O error occurs
*/
- @java.io.Serial
- private void writeObject(java.io.ObjectOutputStream s)
- throws java.io.IOException {
+ @Serial
+ private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(count);
char[] val = new char[capacity()];
@@ -486,13 +489,16 @@ public final class StringBuilder
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
- @java.io.Serial
- private void readObject(java.io.ObjectInputStream s)
- throws IOException, ClassNotFoundException {
+ @Serial
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
s.defaultReadObject();
- count = s.readInt();
+ int c = s.readInt();
char[] val = (char[]) s.readObject();
+ if (c < 0 || c > val.length) {
+ throw new StreamCorruptedException("count value invalid");
+ }
initBytes(val, 0, val.length);
+ count = c;
}
-
}
diff --git a/src/java.base/share/classes/java/util/Hashtable.java b/src/java.base/share/classes/java/util/Hashtable.java
index c103df55790..2b6f6e72540 100644
--- a/src/java.base/share/classes/java/util/Hashtable.java
+++ b/src/java.base/share/classes/java/util/Hashtable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1254,7 +1254,7 @@ public class Hashtable
* Reconstitute the Hashtable from a stream (i.e., deserialize it).
*/
@java.io.Serial
- private void readObject(java.io.ObjectInputStream s)
+ private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
readHashtable(s);
}
@@ -1263,14 +1263,16 @@ public class Hashtable
* Perform deserialization of the Hashtable from an ObjectInputStream.
* The Properties class overrides this method.
*/
- void readHashtable(java.io.ObjectInputStream s)
+ void readHashtable(ObjectInputStream s)
throws IOException, ClassNotFoundException {
- // Read in the threshold and loadFactor
- s.defaultReadObject();
- // Validate loadFactor (ignore threshold - it will be re-computed)
- if (loadFactor <= 0 || Float.isNaN(loadFactor))
- throw new StreamCorruptedException("Illegal Load: " + loadFactor);
+ ObjectInputStream.GetField fields = s.readFields();
+
+ // Read and validate loadFactor (ignore threshold - it will be re-computed)
+ float lf = fields.get("loadFactor", 0.75f);
+ if (lf <= 0 || Float.isNaN(lf))
+ throw new StreamCorruptedException("Illegal load factor: " + lf);
+ lf = Math.min(Math.max(0.25f, lf), 4.0f);
// Read the original length of the array and number of elements
int origlength = s.readInt();
@@ -1282,13 +1284,13 @@ public class Hashtable
// Clamp original length to be more than elements / loadFactor
// (this is the invariant enforced with auto-growth)
- origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
+ origlength = Math.max(origlength, (int)(elements / lf) + 1);
// Compute new length with a bit of room 5% + 3 to grow but
// no larger than the clamped original length. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
- int length = (int)((elements + elements / 20) / loadFactor) + 3;
+ int length = (int)((elements + elements / 20) / lf) + 3;
if (length > elements && (length & 1) == 0)
length--;
length = Math.min(length, origlength);
@@ -1300,8 +1302,9 @@ public class Hashtable
// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, length);
+ Hashtable.UnsafeHolder.putLoadFactor(this, lf);
table = new Entry,?>[length];
- threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
+ threshold = (int)Math.min(length * lf, MAX_ARRAY_SIZE + 1);
count = 0;
// Read the number of elements and then all the key/value objects
@@ -1315,6 +1318,18 @@ public class Hashtable
}
}
+ // Support for resetting final field during deserializing
+ private static final class UnsafeHolder {
+ private UnsafeHolder() { throw new InternalError(); }
+ private static final jdk.internal.misc.Unsafe unsafe
+ = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final long LF_OFFSET
+ = unsafe.objectFieldOffset(Hashtable.class, "loadFactor");
+ static void putLoadFactor(Hashtable, ?> table, float lf) {
+ unsafe.putFloat(table, LF_OFFSET, lf);
+ }
+ }
+
/**
* The put method used by readObject. This is provided because put
* is overridable and should not be called in readObject since the
diff --git a/src/java.base/share/classes/java/util/IdentityHashMap.java b/src/java.base/share/classes/java/util/IdentityHashMap.java
index 34d6722ac09..4795c30b3d5 100644
--- a/src/java.base/share/classes/java/util/IdentityHashMap.java
+++ b/src/java.base/share/classes/java/util/IdentityHashMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package java.util;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -1267,12 +1269,12 @@ public class IdentityHashMap
* particular order.
*/
@java.io.Serial
- private void writeObject(java.io.ObjectOutputStream s)
+ private void writeObject(ObjectOutputStream s)
throws java.io.IOException {
- // Write out and any hidden stuff
+ // Write out size (number of mappings) and any hidden stuff
s.defaultWriteObject();
- // Write out size (number of Mappings)
+ // Write out size again (maintained for backward compatibility)
s.writeInt(size);
// Write out keys and values (alternating)
@@ -1291,18 +1293,20 @@ public class IdentityHashMap
* deserializes it).
*/
@java.io.Serial
- private void readObject(java.io.ObjectInputStream s)
+ private void readObject(ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
- // Read in any hidden stuff
- s.defaultReadObject();
+ // Size (number of mappings) is written to the stream twice
+ // Read first size value and ignore it
+ s.readFields();
- // Read in size (number of Mappings)
+ // Read second size value, validate and assign to size field
int size = s.readInt();
if (size < 0)
throw new java.io.StreamCorruptedException
("Illegal mappings count: " + size);
int cap = capacity(size);
- SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap);
+ SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap*2);
+ this.size = size;
init(cap);
// Read the keys and values, and put the mappings in the table
diff --git a/src/java.base/share/classes/java/util/jar/Attributes.java b/src/java.base/share/classes/java/util/jar/Attributes.java
index a33d1086285..3b59f74275f 100644
--- a/src/java.base/share/classes/java/util/jar/Attributes.java
+++ b/src/java.base/share/classes/java/util/jar/Attributes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
package java.util.jar;
+import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collection;
@@ -366,7 +367,7 @@ public class Attributes implements Map
* @xsl.usage internal
+ *
+ * @LastModified: Sept 2021
*/
public class SystemIDResolver
{
@@ -275,7 +276,7 @@ public class SystemIDResolver
public static String getAbsoluteURI(String urlString, String base)
throws TransformerException
{
- if (base == null)
+ if (base == null || base.length() == 0)
return getAbsoluteURI(urlString);
String absoluteBase = getAbsoluteURI(base);
diff --git a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java
index 853be197754..e178e646d7d 100644
--- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java
+++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java
@@ -27,6 +27,7 @@ package com.sun.java.accessibility.internal;
import java.awt.*;
import java.awt.event.*;
+import java.awt.geom.AffineTransform;
import java.util.*;
import java.lang.*;
import java.lang.reflect.*;
@@ -478,6 +479,9 @@ public final class AccessBridge {
if (parent == null) {
return null;
}
+ Point userSpaceXY = AccessibilityGraphicsEnvironment.toUserSpace(x, y);
+ x = userSpaceXY.x;
+ y = userSpaceXY.y;
if (windowHandleToContextMap != null &&
windowHandleToContextMap.containsValue(getRootAccessibleContext(parent))) {
// Path for applications that register their top-level
@@ -1593,6 +1597,8 @@ public final class AccessBridge {
if (p != null) {
r.x = p.x;
r.y = p.y;
+
+ r = AccessibilityGraphicsEnvironment.toDeviceSpaceAbs(r);
return r;
}
} catch (Exception e) {
@@ -2257,6 +2263,7 @@ public final class AccessBridge {
if (s != null && s.equals("\n")) {
rect.width = 0;
}
+ rect = AccessibilityGraphicsEnvironment.toDeviceSpaceAbs(rect);
return rect;
}
}
@@ -7338,4 +7345,182 @@ public final class AccessBridge {
}
}
}
+
+ /**
+ * A helper class to handle coordinate conversion between screen and user spaces.
+ * See {@link sun.java2d.SunGraphicsEnvironment}
+ */
+ private static abstract class AccessibilityGraphicsEnvironment extends GraphicsEnvironment {
+ /**
+ * Returns the graphics configuration which bounds contain the given point in the user's space.
+ *
+ * See {@link sun.java2d.SunGraphicsEnvironment#getGraphicsConfigurationAtPoint(GraphicsConfiguration, double, double)}
+ *
+ * @param x the x coordinate of the given point in the user's space
+ * @param y the y coordinate of the given point in the user's space
+ * @return the graphics configuration
+ */
+ public static GraphicsConfiguration getGraphicsConfigurationAtPoint(double x, double y) {
+ GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ .getDefaultScreenDevice().getDefaultConfiguration();
+ return getGraphicsConfigurationAtPoint(gc, x, y);
+ }
+
+ /**
+ * Returns the graphics configuration which bounds contain the given point in the user's space.
+ *
+ * See {@link sun.java2d.SunGraphicsEnvironment#getGraphicsConfigurationAtPoint(GraphicsConfiguration, double, double)}
+ *
+ * @param current the default configuration which is checked in the first
+ * place
+ * @param x the x coordinate of the given point in the user's space
+ * @param y the y coordinate of the given point in the user's space
+ * @return the graphics configuration
+ */
+ public static GraphicsConfiguration getGraphicsConfigurationAtPoint(
+ GraphicsConfiguration current, double x, double y) {
+ if (containsUserSpacePoint(current, x, y)) {
+ return current;
+ }
+ GraphicsEnvironment env = getLocalGraphicsEnvironment();
+ for (GraphicsDevice device : env.getScreenDevices()) {
+ GraphicsConfiguration config = device.getDefaultConfiguration();
+ if (containsUserSpacePoint(config, x, y)) {
+ return config;
+ }
+ }
+ return current;
+ }
+
+ /**
+ * Returns the graphics configuration which bounds contain the given point in the device space.
+ *
+ * @param x the x coordinate of the given point in the device space
+ * @param y the y coordinate of the given point in the device space
+ * @return the graphics configuration
+ */
+ public static GraphicsConfiguration getGraphicsConfigurationAtDevicePoint(double x, double y) {
+ GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
+ .getDefaultScreenDevice().getDefaultConfiguration();
+ return getGraphicsConfigurationAtDevicePoint(gc, x, y);
+ }
+
+ /**
+ * Returns the graphics configuration which bounds contain the given point in the device space.
+ *
+ * @param current the default configuration which is checked in the first
+ * place
+ * @param x the x coordinate of the given point in the device space
+ * @param y the y coordinate of the given point in the device space
+ * @return the graphics configuration
+ */
+ public static GraphicsConfiguration getGraphicsConfigurationAtDevicePoint(
+ GraphicsConfiguration current, double x, double y) {
+ if (containsDeviceSpacePoint(current, x, y)) {
+ return current;
+ }
+ GraphicsEnvironment env = getLocalGraphicsEnvironment();
+ for (GraphicsDevice device : env.getScreenDevices()) {
+ GraphicsConfiguration config = device.getDefaultConfiguration();
+ if (containsDeviceSpacePoint(config, x, y)) {
+ return config;
+ }
+ }
+ return current;
+ }
+
+ private static boolean containsDeviceSpacePoint(GraphicsConfiguration config, double x, double y) {
+ Rectangle bounds = config.getBounds();
+ bounds = toDeviceSpaceAbs(config, bounds.x, bounds.y, bounds.width, bounds.height);
+ return bounds.contains(x, y);
+ }
+
+ private static boolean containsUserSpacePoint(GraphicsConfiguration config, double x, double y) {
+ Rectangle bounds = config.getBounds();
+ return bounds.contains(x, y);
+ }
+
+ /**
+ * Converts absolute coordinates from the device
+ * space to the user's space space using appropriate device transformation.
+ *
+ * @param x absolute x coordinate in the device's space
+ * @param y absolute y coordinate in the device's space
+ * @return the corresponding coordinates in user's space
+ */
+ public static Point toUserSpace(int x, int y) {
+ GraphicsConfiguration gc = getGraphicsConfigurationAtDevicePoint(x, y);
+ return toUserSpace(gc, x, y);
+ }
+
+ /**
+ * Converts absolute coordinates from the device
+ * space to the user's space using passed graphics configuration.
+ *
+ * @param gc the graphics configuration to be used for transformation
+ * @param x absolute x coordinate in the device's space
+ * @param y absolute y coordinate in the device's space
+ * @return the corresponding coordinates in user's space
+ */
+ public static Point toUserSpace(GraphicsConfiguration gc, int x, int y) {
+ AffineTransform tx = gc.getDefaultTransform();
+ Rectangle screen = gc.getBounds();
+ int userX = screen.x + clipRound((x - screen.x) / tx.getScaleX());
+ int userY = screen.y + clipRound((y - screen.y) / tx.getScaleY());
+ return new Point(userX, userY);
+ }
+
+ /**
+ * Converts the rectangle from the user's space to the device space using
+ * appropriate device transformation.
+ *
+ * See {@link sun.java2d.SunGraphicsEnvironment#toDeviceSpaceAbs(Rectangle)}
+ *
+ * @param rect the rectangle in the user's space
+ * @return the rectangle which uses device space (pixels)
+ */
+ public static Rectangle toDeviceSpaceAbs(Rectangle rect) {
+ GraphicsConfiguration gc = getGraphicsConfigurationAtPoint(rect.x, rect.y);
+ return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height);
+ }
+
+ /**
+ * Converts absolute coordinates (x, y) and the size (w, h) from the user's
+ * space to the device space using passed graphics configuration.
+ *
+ * See {@link sun.java2d.SunGraphicsEnvironment#toDeviceSpaceAbs(GraphicsConfiguration, int, int, int, int)}
+ *
+ * @param gc the graphics configuration to be used for transformation
+ * @param x absolute coordinate in the user's space
+ * @param y absolute coordinate in the user's space
+ * @param w the width in the user's space
+ * @param h the height in the user's space
+ * @return the rectangle which uses device space (pixels)
+ */
+ public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc,
+ int x, int y, int w, int h) {
+ AffineTransform tx = gc.getDefaultTransform();
+ Rectangle screen = gc.getBounds();
+ return new Rectangle(
+ screen.x + clipRound((x - screen.x) * tx.getScaleX()),
+ screen.y + clipRound((y - screen.y) * tx.getScaleY()),
+ clipRound(w * tx.getScaleX()),
+ clipRound(h * tx.getScaleY())
+ );
+ }
+
+ /**
+ * See {@link sun.java2d.pipe.Region#clipRound}
+ */
+ private static int clipRound(final double coordinate) {
+ final double newv = coordinate - 0.5;
+ if (newv < Integer.MIN_VALUE) {
+ return Integer.MIN_VALUE;
+ }
+ if (newv > Integer.MAX_VALUE) {
+ return Integer.MAX_VALUE;
+ }
+ return (int) Math.ceil(newv);
+ }
+ }
}
diff --git a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
index a21ce9fa73b..f08a7c004b3 100644
--- a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
+++ b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -399,6 +399,7 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_enqueue
/*
* Setup data to copy to target process
*/
+ memset(&data, 0, sizeof(data));
data._GetModuleHandle = _GetModuleHandle;
data._GetProcAddress = _GetProcAddress;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
index 56fdc73e02e..48a575658b3 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java
@@ -665,11 +665,14 @@ public class TransPatterns extends TreeTranslator {
@Override
public void visitClassDef(JCClassDecl tree) {
ClassSymbol prevCurrentClass = currentClass;
+ MethodSymbol prevMethodSym = currentMethodSym;
try {
currentClass = tree.sym;
+ currentMethodSym = null;
super.visitClassDef(tree);
} finally {
currentClass = prevCurrentClass;
+ currentMethodSym = prevMethodSym;
}
}
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
index c875a1ab9b4..ac6fb7be005 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthInputStream.java
@@ -41,6 +41,9 @@ class FixedLengthInputStream extends LeftOverInputStream {
FixedLengthInputStream (ExchangeImpl t, InputStream src, long len) {
super (t, src);
+ if (len < 0) {
+ throw new IllegalArgumentException("Content-Length: " + len);
+ }
this.remaining = len;
}
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
index 8b431645ceb..4935214c2e1 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/FixedLengthOutputStream.java
@@ -47,6 +47,9 @@ class FixedLengthOutputStream extends FilterOutputStream
FixedLengthOutputStream (ExchangeImpl t, OutputStream src, long len) {
super (src);
+ if (len < 0) {
+ throw new IllegalArgumentException("Content-Length: " + len);
+ }
this.t = t;
this.remaining = len;
}
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
index 265b5c82718..92b2f709c34 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -208,7 +208,9 @@ class Request {
"sun.net.httpserver.maxReqHeaders) exceeded, " +
ServerConfig.getMaxReqHeaders() + ".");
}
-
+ if (k == null) { // Headers disallows null keys, use empty string
+ k = ""; // instead to represent invalid key
+ }
hdrs.add (k,v);
len = 0;
}
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
index 03082fdb59b..c2ed3e0e9c6 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
@@ -630,6 +630,11 @@ class ServerImpl implements TimeSource {
headerValue = headers.getFirst("Content-Length");
if (headerValue != null) {
clen = Long.parseLong(headerValue);
+ if (clen < 0) {
+ reject(Code.HTTP_BAD_REQUEST, requestLine,
+ "Illegal Content-Length value");
+ return;
+ }
}
if (clen == 0) {
requestCompleted(connection);
diff --git a/test/jdk/java/util/Hashtable/DeserializedLength.java b/test/jdk/java/util/Hashtable/DeserializedLength.java
index 376c1f026d0..d515878fd36 100644
--- a/test/jdk/java/util/Hashtable/DeserializedLength.java
+++ b/test/jdk/java/util/Hashtable/DeserializedLength.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -100,7 +100,7 @@ public class DeserializedLength {
);
for (int elements : new int[]{10, 50, 500, 5000}) {
- for (float loadFactor : new float[]{0.15f, 0.5f, 0.75f, 1.0f, 2.5f}) {
+ for (float loadFactor : new float[]{0.25f, 0.5f, 0.75f, 1.0f, 2.5f}) {
ok &= testDeserializedLength(elements, loadFactor);
}
}
diff --git a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java
index af7493212ca..7bf389c75ad 100644
--- a/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java
+++ b/test/jdk/jdk/jfr/jmx/streaming/TestMaxSize.java
@@ -70,12 +70,17 @@ public class TestMaxSize {
while (directorySize(dir) < 50_000_000) {
emitEvents(500_000);
}
+ System.out.println("Before setMaxSize(1_000_000)");
+ fileCount(dir);
e.setMaxSize(1_000_000);
+ System.out.println("After setMaxSize(1_000_000)");
long count = fileCount(dir);
- if (count > 2) {
- // Two chunks can happen when header of new chunk is written and previous
- // chunk is not finalized.
- throw new Exception("Expected only one or two chunks with setMaxSize(1_000_000). Found " + count);
+ if (count > 3) {
+ // Three files can happen when:
+ // File 1: Header of new chunk is written to disk
+ // File 2: Previous chunk is not yet finalized and added to list of DiskChunks
+ // File 3: Previous previous file is in the list of DiskChunks.
+ throw new Exception("Expected at most three chunks with setMaxSize(1_000_000). Found " + count);
}
finished.set(true);
}
@@ -94,21 +99,24 @@ public class TestMaxSize {
System.out.println("Files:");
AtomicInteger count = new AtomicInteger();
Files.list(dir).forEach(p -> {
- System.out.println(p);
+ System.out.println(p + " " + fileSize(p));
count.incrementAndGet();
});
return count.get();
}
private static long directorySize(Path dir) throws IOException {
- long p = Files.list(dir).mapToLong(f -> {
- try {
- return Files.size(f);
- } catch (IOException e) {
- return 0;
- }
- }).sum();
+ long p = Files.list(dir).mapToLong(f -> fileSize(f)).sum();
System.out.println("Directory size: " + p);
return p;
}
+
+ private static long fileSize(Path p) {
+ try {
+ return Files.size(p);
+ } catch (IOException e) {
+ System.out.println("Could not determine file size for " + p);
+ return 0;
+ }
+ }
}
diff --git a/test/langtools/tools/javac/patterns/BindingsInitializer.java b/test/langtools/tools/javac/patterns/BindingsInitializer.java
new file mode 100644
index 00000000000..eaf550c3daf
--- /dev/null
+++ b/test/langtools/tools/javac/patterns/BindingsInitializer.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8278834
+ * @summary Verify pattern matching nested inside initializers of classes nested in methods
+ * works correctly.
+ * @library /tools/lib /tools/javac/lib
+ * @modules
+ * jdk.compiler/com.sun.tools.javac.api
+ * jdk.compiler/com.sun.tools.javac.file
+ * jdk.compiler/com.sun.tools.javac.main
+ * jdk.compiler/com.sun.tools.javac.util
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @build combo.ComboTestHelper
+ * @compile BindingsInitializer.java
+ * @run main BindingsInitializer
+ */
+
+import combo.ComboInstance;
+import combo.ComboParameter;
+import combo.ComboTask;
+import combo.ComboTestHelper;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import toolbox.ToolBox;
+
+public class BindingsInitializer extends ComboInstance {
+ protected ToolBox tb;
+
+ BindingsInitializer() {
+ super();
+ tb = new ToolBox();
+ }
+
+ public static void main(String... args) throws Exception {
+ new ComboTestHelper()
+ .withDimension("OUTER", (x, outer) -> x.outer = outer, Outer.values())
+ .withDimension("MIDDLE", (x, middle) -> x.middle = middle, Middle.values())
+ .withDimension("INNER", (x, inner) -> x.inner = inner, Inner.values())
+ .withDimension("TEST", (x, test) -> x.test = test, Test.values())
+ .run(BindingsInitializer::new);
+ }
+
+ private Outer outer;
+ private Middle middle;
+ private Inner inner;
+ private Test test;
+
+ private static final String MAIN_TEMPLATE =
+ """
+ public class Test {
+ private static Object obj = "";
+ #{OUTER}
+ }
+ """;
+
+ @Override
+ protected void doWork() throws Throwable {
+ Path base = Paths.get(".");
+
+ ComboTask task = newCompilationTask()
+ .withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) {
+ case "OUTER" -> outer;
+ case "MIDDLE" -> middle;
+ case "INNER" -> inner;
+ case "TESST" -> test;
+ default -> throw new UnsupportedOperationException(pname);
+ });
+
+ task.generate(result -> {
+ if (result.hasErrors()) {
+ throw new AssertionError("Unexpected result: " + result.compilationInfo());
+ }
+ });
+ }
+
+ public enum Outer implements ComboParameter {
+ NONE("#{MIDDLE}"),
+ STATIC_CLASS("static class Nested { #{MIDDLE} }"),
+ CLASS("class Inner { #{MIDDLE} }");
+ private final String code;
+
+ private Outer(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return code;
+ }
+ }
+
+ public enum Middle implements ComboParameter {
+ STATIC_INIT("static { #{INNER} }"),
+ INIT("{ #{INNER} }"),
+ METHOD("void test() { #{INNER} }");
+ private final String code;
+
+ private Middle(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return code;
+ }
+ }
+
+ public enum Inner implements ComboParameter {
+ DIRECT("#{TEST}"),
+ CLASS_STATIC_INIT("class C { static { #{TEST} } }"),
+ CLASS_INIT("class C { { #{TEST} } }"),
+ CLASS_METHOD("class C { void t() { #{TEST} } }"),
+ ANNONYMOUS_CLASS_STATIC_INIT("new Object() { static { #{TEST} } };"),
+ ANNONYMOUS_CLASS_INIT("new Object() { { #{TEST} } };"),
+ ANNONYMOUS_CLASS_METHOD("new Object() { void t() { #{TEST} } };");
+ private final String code;
+
+ private Inner(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return code;
+ }
+ }
+
+ public enum Test implements ComboParameter {
+ TEST("if (obj instanceof String str) System.err.println(str);");
+ private final String code;
+
+ private Test(String code) {
+ this.code = code;
+ }
+
+ @Override
+ public String expand(String optParameter) {
+ return code;
+ }
+ }
+}